KnockOutJS - Multiple ViewModels in a single View
Asked Answered
P

5

204

I'm thinking that my application is getting quite large now, too large to handle each View with a single ViewModel.

So I'm wondering how difficult it would be to create multiple ViewModels and load them all into a single View. With a note that I also need to be able to pass X ViewModel data into Y ViewModel data so the individual ViewModels need to be able to communicate with each other or at least be aware of each other.

For instance I have a <select> drop down, that select drop down has a selected state which allows me to pass the ID of the selected item in the <select> to another Ajax call in a separate ViewModel....

Any points on dealing with numerous ViewModels in a single View appreciated :)

Pioneer answered 15/2, 2012 at 12:52 Comment(0)
A
291

Knockout now supports multiple model binding. The ko.applyBindings() method takes an optional parameter - the element and its descendants to which the binding will be activated.

For example:

ko.applyBindings(myViewModel, document.getElementById('someElementId'))

This restricts the activation to the element with ID someElementId and its descendants.

See documentation for more details.

Averyaveryl answered 20/7, 2012 at 2:36 Comment(4)
If you wish to use a jQuery selector, you'll want to add [0] to specify an actual DOM element (instead of the jQuery object) like so: ko.applyBindings(myViewModel, $('#someElementId')[0])Walleyed
This should be the accepted answer. You could still use a master object like the currently accepted answer has, and then bind the individual viewmodels to their appropriate elements on the page. This will save on performance, and limits the scope needed for data-bind.Effective
Is it possible to communicate viewModels each other with this approach? i.e. I have TaskVM and NoteVM. Task can have Notes. Therefore my TaskVM must have an observableArray namely notes whose type is TaskVM. Can you share an example for a case like that?Jago
It's probably best to ask about communication between VMs in a new question.Periwig
G
150

If they all need to be on the same page, one easy way to do this is to have a master view model containing an array (or property list) of the other view models.

masterVM = {
    vmA : new VmA(),
    vmB : new VmB(),
    vmC : new VmC(),
}

Then your masterVM can have other properties if needed, for the page itself. Communication between the view models would not be difficult in this situation as you could relay through the masterVM, or you could use the $parent / $root in bindings, or some other custom options.

Gisellegish answered 15/2, 2012 at 13:59 Comment(8)
So would I be able to do something like: data-bind="text: masterVM.vmA", I suppose I could still use ko.applyBindings with the DOM element attached. Presume that would also mean I could do: data-bind="$parent.masterVm"?Pioneer
@Pioneer You can use with: bindging, so You will not repeat yourselfSolidago
@Pioneer Yes, you could do that if you bound to the masterVM. You can also use the "with" binding to help avoid the dot syntax when you dive into the sub view models.Gisellegish
Better explained here, I guess: #7343314Bribe
Great question, and the fact that this works proves how awesome knockout.js really is.Gesticulation
I think this answer provides the most flexible solution as it allow you to access various viewmodels from within the same scope. With the other common approach, viewmodels cannot overlap causing other headaches.Excurvature
I think this approach is a very restrictive one ... Now in my case i am using ASP.Net MVC4, this doesnt help since there will be partial views having its own ViewModels , and the partial/Content sections , shouldnt interfere with each other, and due to conditional rendering It will be really difficult to use this approach.Machute
@Machute using <!-- ko stopBinding: true --> will help you with that multiple view models and partial views sections. See knockmeout.net/2012/05/quick-tip-skip-binding.html for more information.Pleat
J
24

This is my answer after completing very large project with lots of ViewModels in single view.

Html View

    <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <div id="container1">
        <ul>
            <li >Container1 item</li>
            <!-- ko foreach: myItems -->
            <li>Item <span data-bind="text: $data"></span></li>
            <!-- /ko -->
        </ul>
    </div>

    <div id="container2">
        <ul>
            <li >Container2 item</li>
            <!-- ko foreach: myItems -->
                <li>Item <span data-bind="text: $data"></span></li>
            <!-- /ko -->
        </ul>
    </div>

    <script src="js/jquery-1.11.1.js"></script>
    <script src="js/knockout-3.0.0.js"></script>
    <script src="js/DataFunction.js"></script>
    <script src="js/Container1ViewModel.js"></script>
    <script src="js/Container2ViewModel.js"></script>

</body>
</html>

For this view I'm creating 2 view models for id=container1 and id=container2 in two separate javascript files.

Container1ViewModel.js

function Container1ViewModel()
{
    var self = this;
    self.myItems = ko.observableArray();
    self.myItems.push("ABC");
    self.myItems.push("CDE");

} 

Container2ViewModel.js

function Container2ViewModel() {
    var self = this;
    self.myItems = ko.observableArray();
    self.myItems.push("XYZ");
    self.myItems.push("PQR");

}

Then after these 2 viewmodels are registering as separate viewmodels in DataFunction.js

var container1VM;
var container2VM;

$(document).ready(function() {

    if ($.isEmptyObject(container1VM)) {
        container1VM = new Container1ViewModel();
        ko.applyBindings(container1VM, document.getElementById("container1"));
    }

    if ($.isEmptyObject(container2VM)) {
        container2VM = new Container2ViewModel();
        ko.applyBindings(container2VM, document.getElementById("container2"));
    }
});

Like this you can add any number of viewmodels for separate divs. But make sure do not create separate view model for a div inside registered div.

Jumbala answered 15/2, 2012 at 12:52 Comment(2)
Does is possible to do it kind of viewmodel inside of other instead to be separate elements of the DOM?Noto
This solution gets the trophy for me. I am working on a complex UI requiring the generation of endless amounts of forms that can change based on criteria entered by the user and some based on context clues provided by collected data. All the other solutions restricted me in one way or another.Voelker
A
4

Check MultiModels plugin for Knockout JS - https://github.com/sergun/Knockout-MultiModels

Awed answered 27/6, 2012 at 16:9 Comment(3)
What advantage does this have over just ko.applyBindings(viewModel, document.getElementById("divName"))? Isn't it just syntactic sugar?Grubbs
@Paolo del Mundo It also adds a dependancy on the LiveQuery plugin.Dodger
@PaolodelMundo the purpose of the plugin is to be able to use set of viewmodels in decalrative wayAwed
A
0

We use components to achieve that. (http://knockoutjs.com/documentation/component-overview.html)

For example, we have this component library we are developing: https://github.com/EDMdesigner/knobjs

If you dig into the code, you will see that for example we reuse the knob-button component in several places.

Archimandrite answered 28/6, 2016 at 20:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.