翻译原文: http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html
问题:如何在一个页面中绑定多个view models。
通常的做法是通过调用ko.applyBindings(vm,containerNode)方法将view model 绑定到特定的根元素节点上,然而,这种方法的限制在于当你需要邦定多个view model时,这些已使用
过的容器元素(containerNode)不能被再次使用。意味着你不能嵌套绑定viewmodel。
解决这个问题的一个方法是定义一个顶级viewmodel来容纳其所有的“sub” view models,然后页面全局范围内调用ko.applyBindings。
Recently, I worked with several people on questions related to binding multiple view models in a single page. One common approach is to bind a view model to a particular root element using a call like ko.applyBindings(vm, containerNode);
. However, a limitation with this approach is that when binding multiple view models, none of the container elements can overlap. This means that you could not bind one view model nested inside of another.
One way to address this issue is to create a top level view model that contains your “sub” view models and then call ko.applyBindings
on the entire page with the overall view model:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
在视图中,你便可以通过使用with binding方法和$root来绑定嵌套的view model。
Now in the view, you can use the with
binding along with $root
to bind a nested view model:
1 2 3 4 5 6 7 |
|
这种方法好处在于让你只需通过调用一次ko.applyBinding方法,你可以随时使用$root,$parent,parents来从特定的sub view model获取数据。然而,如果希望维护模块化的代码以及更好地控制怎样及何时绑定元素,使用这种创建顶级view model的方法便
显得不便捷也不实用了。
在Knockout 2.0中,有一个简单的替代方案能提供更好的便利。Bindings方法能在其init方法中返回一个controlsDescendantBindings标志用于指示当前binding循环不要试着绑定其子元素。这个标志也在template及control-flow bindings中有用到,它们使用
适当的data context来处理绑定其子元素。
在我们的方案中,我们将利用这个标志来自定义绑定并简单告知Knockout忽略对于特点块的绑定。
This technique is nice, because you only have to make a single ko.applyBindings
call and you can use $root
or $parent
/$parents
to access data at any time from another view model. However, based on a desire to maintain modular code and to control how and when elements are bound, it is often not convenient or practical to build a top level view model.
With Knockout 2.0, there is a simple alternative that can provide for greater flexibility. Bindings are now able to return a flag called controlsDescendantBindings
in their init
function to indicate that the current binding loop should not try to bind this element’s children. This flag is used by the template
and control-flow bindings (wrappers to the template
binding), as they will handle binding their own children with an appropriate data context.
For our scenario, we can take advantage of this flag and simply tell Knockout to leave a certain section alone by using a simple custom binding:
1 2 3 4 5 |
|
现在,我们可以将“shell”model绑定到这个页面,而“profile”model 绑定到特定的容器中去。
Now, we can bind our “shell” model to the entire page and bind our “profile” model to the specific container:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
现在,在我们的视图中,我们就可以使用简单的stopBinding自定义绑定我们的内部容器元素。
In our view, we can now use the simple stopBinding
custom binding around our inner container element:
1 2 3 4 5 6 7 8 9 10 |
|
增加额外的div元素来放置我们的stopBinding方法并不会给我们的应用程序,但是如果使用新版的KO2.1我们可以通过给我们的binding添加ko.virtualElements.allowedBindings方法来创建容器无关的自定义绑定。
Adding the extra div to hold our stopBinding
binding may not cause our app any problems, but if it does then in KO 2.1 we can now create containerless custom bindings by adding our binding to ko.virtualElements.allowedBindings
.
1 2 3 4 5 6 7 |
|
最终我们可以如下简化视图。
and finally we can clean up our view to look like:
1 2 3 4 5 6 7 8 9 |
|
通过这个简单的绑定,现在我们能给页面绑定多个view models而不用担心bindings之间的冲突和重叠了。
以下是一个来自jsFiddle.net的示例:
With this simple binding, we can now compose pages with multiple view models without the worry of conflicting/overlapping bindings.
Here is a live sample:
Link to full sample on jsFiddle.net
ps:项目中一些特定的通用的局部视图如显示 “类别栏” 可以分离出来在各个页面中重复使用,做到DRY。