2

Nested components presuppose nested view models also.

However, in the sample components, I don't see this kinda of dependency to appear (except the BackboneJS TODO app which is not very clear for a KO user).

Could you elaborate on how to do such a design, e.g. for a collection:

  • ItemViewModel with properties Name and IsSelected
  • CollectionViewModel with has an Items property that contains a collection of ItemViewModel and SelectedCount which is computed by counting how many items are selected. (I know this can be done with KO in a simpler way, but for the sake of illustration.
georgiosd
  • 3,038
  • 3
  • 39
  • 51
  • Have a look at the clickcounter example, It has two nested components within the parent component. These nested components do not share the same viewmodel, but communicate via events. On the other hand two subcomponents in salesDashboard component do share the single view model of the parent component. Did I answer? OR am I missing the question btw? – Hasith Sep 21 '12 at 08:52
  • I edited my question to be more specific. Events or sharing a single view model is different than nested view models - this would be closer to how you would do MVVM in Silverlight. – georgiosd Sep 21 '12 at 08:57
  • 2
    Just for your information: Have a look at the "with" binding in knockoutjs (http://knockoutjs.com/documentation/with-binding.html). Here you can have a single parent view model, nesting sub viewmodels in it for each of your component. Then in your component view, you can use 'with' binding to specify which of the sub viewmodel it needs a binding with. – Hasith Sep 21 '12 at 09:02
  • Hm, yes... I suppose that would suffice. I'll test and report back. – georgiosd Sep 21 '12 at 09:06
  • Hm... but how would I pass the sub-viewmodel to the component's `initialize()`? Can you provide a full example? – georgiosd Oct 02 '12 at 12:50
  • I'm not really sure if someone should go along that way to pass viewmodels in to other view models.... that will make things too coupled, and loose the benefit of separating viewmodels. May eb you should either use a large shared view model OR go with components that communicate via messages.. – Hasith Oct 04 '12 at 15:50

2 Answers2

2

Viewmodels (VMs) are just objects (that get bound using ko.applyBindings()) - which means you can arbitrarily nest VMs into a parent object (what @Hasith said). You only need to pass one parent object back to Boilerplate. Bear with some ultra commented code:

// Let's assume you have your items nicely formatted in an array 
// data source (and technically the objects in this array can be 
// considered 'dumb' viewmodels)
var items = [
  {Name:'a',isSelected:false}, 
  {Name:'b',isSelected:true}
  ]

// We can apply some bindings to make 'smarter' viewmodels
// One approach is just to map using rocking KO mapping plugin
var koMapItems = ko.mapping.fromJS( items )

// You could skip the mapping plugin and go for broke
// creating your own VM mappings manually
// (you could do this using the mapping plugin with less work)
var goforbrokeVM = function( item )
{
  var _name = ko.observable( item.Name )
  var _dance = function() { return _name+' is space monkey' }

  return {
    Name: _name,
    isSelected: ko.observable( item.isSelected ),
    spaceMonkey: _dance
  }
}
// Don't forget to assign and create your goforbrokeVMs
var epicItemVMs = []
for (var i=0;i<items.length;i++) 
  epicItemVMs.push( new goforbrokeVM( items[i]) )


// Now the interesting part, lets create a 'child' VM that
// we can embed in another VM. Notice this VM has as a
// property an array of VMs itself.
var childVM = {
  elements: epicItemVMs,
  // A sub method, because we can
  otherMethod: function() { return 'wat' }
}

// And ultimately our 'master' VM with its own properties
// including the nested child VM we setup above (which you'll
// remember contains its own sub VMs)
var ParentVM = {
  // And its own property
  parentPropA: ko.observable('whatever'),

  // Oooow look, a list of alternative ko.mapping VMs
  simpleMappedItems: koMapItems,

  // And a nested VM with its own nested goforbrokeVMs
  nested: childVM
}

// Apply your master viewmodel to the appropriate DOM el.
ko.applyBindings( ParentVM, document.getElementById('items'))

And your HTML:

<div id="items">
  <span data-bind="text: parentPropA"></span>

  <!-- Step through one set of items in ParentVM -->
  <div data-bind="foreach: simpleMappedItems">
    <input type="checkbox" data-bind="checked: isSelected">
    <span data-bind="text: Name"></span>
  </div>

  <!-- Or jump into the nested models and go crazy -->
  <!-- ko with: nested -->
    <div data-bind="foreach:elements">
      <div data-bind="text: spaceMonkey"></div>
    </div>
    <div data-bind="text: otherMethod"></div>
  <!-- /ko -->
</div>

In this way you can pass a single object (in this case ParentVM) to Boilerplate with as many nested viewmodels as you need.

Info for mapping plugin for knockout lives here: http://knockoutjs.com/documentation/plugins-mapping.html

papercowboy
  • 3,369
  • 2
  • 28
  • 32
-1

The 'todo' sample is done by adopting Addy Osmani's implementation. There is an implementation of knockoutjs also here.

janith
  • 1,025
  • 5
  • 21
  • Right. But I am not interested in how to implement a TODO with KO, that's fairly obvious - only how to make it work with the BoilerplateJS structure. – georgiosd Sep 21 '12 at 09:05