我读过很多关于Backbone没有双向约束的内容,但我并不完全理解这个概念。
有人能给我一个例子,说明双向绑定在MVC代码库中是如何工作的,以及它如何与Backbone无关?
答案 0 :(得分:220)
双向绑定仅表示:
Backbone没有#2的“烘焙”实现(虽然你当然可以使用事件监听器来实现)。其他框架like Knockout do wire up two-way binding automagically。
在Backbone中,您可以通过将视图的“渲染”方法绑定到其模型的“更改”事件来轻松实现#1。要实现#2,您还需要向input元素添加更改侦听器,并在处理程序中调用model.set
。
Here's a Fiddle在Backbone中设置了双向绑定。
答案 1 :(得分:43)
双向绑定意味着影响模型的任何与数据相关的更改都会立即传播到匹配的视图中,并且视图中所做的任何更改(例如,通过用户)在基础模型中立即反映。当应用数据发生变化时,用户界面也会发生变化,反之亦然。
这是构建Web应用程序的一个非常可靠的概念,因为它使“模型”抽象成为在应用程序中的任何位置使用的安全的原子数据源。比如,如果绑定到视图的模型发生更改,那么其匹配的UI(视图)将反映出,无论。并且匹配的UI(视图)可以安全地用作收集用户输入/数据的方式,以便使应用程序数据保持最新。
从开发人员的角度来看,一个好的双向绑定实现显然应该使模型和一些视图之间的连接尽可能简单。
然后说Backbone不支持双向绑定是非常 untrue :虽然不是框架的核心功能,但它可以非常简单地使用但是Backbone的事件。对于简单的情况,它需要花费一些明确的代码行;并且对于更复杂的绑定可能变得非常危险。这是一个简单的案例(未经测试的代码,仅为了说明而动态编写):
Model = Backbone.Model.extend
defaults:
data: ''
View = Backbone.View.extend
template: _.template("Edit the data: <input type='text' value='<%= data %>' />")
events:
# Listen for user inputs, and edit the model.
'change input': @setData
initialize: (options) ->
# Listen for model's edition, and trigger UI update
@listenTo @model, 'change:data', @render
render: ->
@$el.html @template(@model.attributes)
@
setData: (e) =>
e.preventDefault()
@model.set 'data', $(e.currentTarget).value()
model: new Model()
view = new View {el: $('.someEl'), model: model}
这是原始Backbone应用程序中非常典型的模式。可以看出,它需要相当数量的(非常标准的)代码。
AngularJS和其他一些替代方案(Ember,Knockout ...)提供双向绑定作为第一公民功能。他们在一些DSL下抽象了许多边缘案例,并尽力在他们的生态系统中集成双向绑定。我们的示例使用AngularJS(未经测试的代码,见上文)看起来像这样:
<div ng-app="app" ng-controller="MainCtrl">
Edit the data:
<input name="mymodel.data" ng-model="mymodel.data">
</div>
angular.module('app', [])
.controller 'MainCtrl', ($scope) ->
$scope.mymodel = {data: ''}
相当简短!
但是,请注意,Backbone的一些完全成熟的双向绑定扩展确实存在(以复杂性降低的原始,主观顺序):{{ 3}},Epoxy,Stickit ...
例如,Epoxy的一个很酷的事情是,它允许您在模板(DOM)内或视图实现(JavaScript)中声明绑定(模型属性&lt; - &gt;视图的DOM元素)。有些人强烈不喜欢在DOM /模板中添加“指令”(例如AngularJS所需的ng- *属性,或Ember的数据绑定属性)。以Epoxy为例,可以将原始的Backbone应用程序重新编写为类似的东西(...):
Model = Backbone.Model.extend
defaults:
data: ''
View = Backbone.Epoxy.View.extend
template: _.template("Edit the data: <input type='text' />")
# or, using the inline form: <input type='text' data-bind='value:data' />
bindings:
'input': 'value:data'
render: ->
@$el.html @template(@model.attributes)
@
model: new Model()
view = new View {el: $('.someEl'), model: model}
总而言之,几乎所有“主流”JS框架都支持双向绑定。其中一些,例如Backbone,确实需要一些额外的工作才能使它顺利地工作,但这些都是相同的,并没有强制执行特定的方式来开始。所以这真的是关于你的心态。
此外,您可能对ModelBinder感兴趣,这是一种不同的Web应用程序架构,通过循环模式促进单向绑定。它基于在任何数据更改时快速,全面地重新呈现UI组件的概念,以确保内聚性并使得更容易推理代码/数据流。在同一趋势中,您可能想要检查MVI(模型 - 视图 - 意图)的概念,例如Flux。
答案 2 :(得分:23)
McGarnagle有一个很好的答案,你会想接受他的,但我想我会提到(因为你问过)数据绑定是如何工作的。
通常通过在对数据进行更改时触发事件来实现,然后导致更新侦听器(例如UI)。
双向绑定通过执行此操作两次,需要注意确保您不会陷入事件循环(事件的更新导致另一个事件被触发)。
我会把它放在评论中,但它已经很长了......
答案 3 :(得分:1)
实际上emberjs
支持双向绑定,这是javascript MVC框架最强大的功能之一。您可以在其用户指南中查看binding
的位置。
对于emberjs,创建双向绑定是通过在末尾创建一个字符串为Binding的新属性,然后指定来自全局范围的路径:
App.wife = Ember.Object.create({
householdIncome: 80000
});
App.husband = Ember.Object.create({
householdIncomeBinding: 'App.wife.householdIncome'
});
App.husband.get('householdIncome'); // 80000
// Someone gets raise.
App.husband.set('householdIncome', 90000);
App.wife.get('householdIncome'); // 90000
请注意,绑定不会立即更新。在同步更改之前,Ember等待所有应用程序代码完成运行,因此您可以根据需要多次更改绑定属性,而不必担心在值是瞬态时同步绑定的开销。
希望它有助于扩展所选的原始答案。
答案 4 :(得分:1)
答案 5 :(得分:0)
值得一提的是,有很多不同的解决方案可以提供双向绑定和播放效果。
我对这款模型活页夹感到愉快 - https://github.com/theironcook/Backbone.ModelBinder。它给出了合理的默认值,但是很多自定义的jquery选择器将模型属性映射到输入元素。
上有一个更加扩展的主干扩展/插件列表