202

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

CLiown
  • 13,665
  • 48
  • 124
  • 205
  • 12
    For those arriving at this question, please scroll past the accepted answer. [Knockout now supports multiple binding contexts](http://stackoverflow.com/a/11572094/998328). There is no need for a giant `masterVM`. – Carrie Kendall Apr 10 '15 at 16:04

5 Answers5

289

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.

GôTô
  • 7,974
  • 3
  • 32
  • 43
sanatgersappa
  • 4,319
  • 2
  • 18
  • 13
  • 72
    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])` – MrBoJangles Feb 25 '13 at 20:30
  • 3
    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. – Kevin Heidt Sep 18 '14 at 16:00
  • 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? – ahmet Mar 08 '16 at 22:16
  • It's probably best to ask about communication between VMs in a new question. – Richard Nalezynski Oct 10 '16 at 21:17
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.

Justin
  • 6,611
  • 3
  • 36
  • 57
John Papa
  • 21,880
  • 4
  • 61
  • 60
  • 2
    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"? – CLiown Feb 15 '12 at 14:23
  • 12
    @CLiown You can use `with:` bindging, so You will not repeat yourself – AlfeG Feb 15 '12 at 14:25
  • 4
    @CLiown 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. – John Papa Feb 15 '12 at 15:47
  • Better explained here, I guess: http://stackoverflow.com/questions/7342814/knockoutjs-ko-applybindings-to-partial-view – Mário Rodrigues Apr 16 '12 at 20:01
  • Great question, and the fact that this works proves how awesome knockout.js really is. – Adam Levitt Aug 22 '12 at 18:41
  • 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. – Lasse Christiansen Jan 13 '13 at 10:41
  • 1
    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. – bhuvin Feb 28 '13 at 11:37
  • 1
    @bhuvin using will help you with that multiple view models and partial views sections. See http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html for more information. – Micaël Félix May 26 '14 at 06:40
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.

Janith Widarshana
  • 3,213
  • 9
  • 51
  • 73
  • Does is possible to do it kind of viewmodel inside of other instead to be separate elements of the DOM? – UserEsp Aug 24 '18 at 18:35
  • 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. – snowYetis Jun 09 '21 at 23:35
4

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

Sergey Zwezdin
  • 386
  • 4
  • 15
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.

gyula.nemeth
  • 847
  • 10
  • 9