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>