Tag Archives: templating

Data-driven Knockout views II

Last time I posted a code snippet with description of data driven Knockout views. I have modified and simplified it and

  • added support for forEach binding and
  • the type member of ViewModel class can return a function that is evaluated for every item in collection

This brings even more flexibility, the view model itself can decide which view to use based on a value returned from type function.

contentBindingHandler.jsview rawview file on Bitbucket

The example will follow soon.

Data-driven Knockout views

I’m using Caliburn.Micro in my Silverlight and WPF projects. I fell in love with Caliburn automagic – view locator, auto binding, etc. For my NodeJS project I was looking for JavaScript library supporting MVVM pattern and I found KnockoutJS.

Knockout has very nice feature called binding with a simple way of implementing your custom binding.

What I was missing was Caliburn’s binding/convention for ContentControl – data driven view rendering:

<!-- DataContext is RootViewModel -->
<ContentControl Name="ChildViewModel" />

with the view located for value of ParentViewModel.ViewModel property and then rendered.

The solution I come with uses

  • Infuser as a templating engine, more on using Infuser as templating engine for Knockout here
  • TrafficCop used by Infuser and prevents duplicate AJAX requests

Infuser is configured to load the view templates from views directory (I will implement some view locator later):

infuser.defaults.templateSuffix = ".tmpl.html";
infuser.defaults.templateUrl = "/js/app/views/"; 

and every view model object must have a property returning its type name (if someone knows about better way how to do it, please let me know).

var SampleViewModel = kb.ViewModel.extend({
  constructor: function(model) {
    ...
    this.type = 'SampleViewModel';
  }, 
  ...
}

Code of the binding handler:

ko.bindingHandlers.content = {
  'init': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    var options = ko.utils.unwrapObservable(valueAccessor())
    var templateName = options.data.type.replace(/viewmodel/ig, 'View'),
      dataValue = ko.utils.unwrapObservable(options['data']);
    var innerBindingContext = bindingContext['createChildContext'](dataValue, options['as']);
    ko.renderTemplate(templateName || element, innerBindingContext, options, element);
  }
};

and the HTML

<div data-bind="content: { data: childViewModel }"></div>
<script>
  var rootViewModel = kb.ViewModel.extend({
    constructor: function(model, options) {
      this.childViewModel = new SampleViewModel();
    }
  });
  ko.applyBindings(rootViewModel);
</script>