An island with a different viewModel

I have a multi-page application with a navigation panel that is used across all pages. Each page has its own view model, the panel view model is the same. I could remember to always bind both models but I was hoping there would be a more universal solution. Some way to bind my one-page model to the whole document, and yet have an island of DOM elements automatically bound to a different model.

Here’s what I knew would work, but I didn’t like:

  1. <script src="page-1.js"></script>
  2. <div id="navigation-panel" data-bind="with: navModel">…</div>
  3. <div id="page1" data-bind="with: page1Model">…</div>
  4.  

The trouble is that I have to remember to bind navModel in page-1.js and all page-x scripts.

  1. var model = {
  2.   ‘navModel’ : new NavModel,
  3.   ‘page1Model’ : new Page1Model
  4. };
  5. ko.applyBindings(model);
  6.  

I didn’t want page-1.js to care about elements that are on every page.

Solution 1: Component

  1. <script src="navigation.js"></script>
  2. <script src="page-1.js"></script>
  3. <script type="text/html" id="template-navigation">
  4.   <div id="navigation-panel">…</div>
  5. </script>
  6. <div data-bind="component: ‘navigation’"></div>
  7. <div id="page1">…</div>

navigation.js:

  1. ko.components.register(‘navigation’, {
  2.  template: { element: ‘template-navigation’ },
  3.  viewModel: NavModel
  4. });

page-1.js:

  1. ko.applyBindings(new Page1Model);

This worked well but I didn’t like the extra markup. Also, I actually have several islands and I didn’t like having to define a component for each.

Solution 2:  Custom binding handler

  1. <script src="navigation.js"></script>
  2. <script src="page-1.js"></script>
  3. <div id="navigation-panel" data-bind="viewModel: navModel">…</div>
  4. <div id="page1">…</div>

navigation.js:

  1. var navModel = new NavModel;
  2. ko.bindingHandlers.viewModel = {
  3.   init: function(element, valueAccessor) {
  4.     ko.applyBindingsToDescendants(valueAccessor(), element);
  5.     return { controlsDescendantBindings: true };
  6.   }
  7. };

page-1.js:

  1. ko.applyBindings(new Page1Model);

Nice and smooth. One handler takes care of all islands, all I have to do is to define a global variable for each view model.

Tags:

Leave a Reply