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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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: