Question

I'm trying to write custom binding based on template binding. Idea is to have template with it's own view model class, but i don't want to add instances of that class to parent view model (at least not in code. adding instances at execution time is ok with me).

To understand what i'm trying to achieve here is example: i want to build advanced slider, i wan't slider's html to be in template, also slider need's it's own class, but i don't want to add slider's view model instance to parent view model. Here is my code, it works at first and seems that it renders template at first, but then exception is thrown and i can't understand why. View working code here

html:

  <script type="text/html" id="my-template">  
    <div data-bind="text: internalValue"></div>
  </script>

  <div data-bind="templateWithModel: {name: 'my-template', dataPropName: 'price', modelConstructor: Slider}">
  </div>

js:

ko.bindingHandlers.templateWithModel = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext){
        var options = ko.utils.unwrapObservable(valueAccessor());
        var modelName = options.name + "_" + options.dataPropName;
        viewModel[modelName] = new options.modelConstructor(viewModel[options.dataPropName]);

        ko.bindingHandlers.template.init(element, valueAccessor);
    },
    'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var options = ko.utils.unwrapObservable(valueAccessor());
        var modelName = options.name + "_" + options.dataPropName;
        options.data = viewModel[modelName];

        ko.bindingHandlers.template.update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
    }
}


var Slider = function(obsVal){
  var self = this;

  self.internalValue = ko.observable(obsVal());
}     

var vm = {price: ko.observable(5)};

ko.applyBindings(vm);
Was it helpful?

Solution

In your init function, you will want to return the value of ko.bindingHandlers.template.init or return { controlsDescendantBindings: true }.

This tells Knockout that the binding will take care of applying bindings to the child elements . Otherwise, Knockout will continue to apply bindings with the overall view model against the child elements, which causes your exception as internalValue is not a property on the top-level view model.

This part of the docs: http://knockoutjs.com/documentation/custom-bindings-controlling-descendant-bindings.html and this blog post help explain controlsDescendantBindings further: http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html

OTHER TIPS

I did a framework for binding viewmodels against templates. Take a look here,

https://github.com/AndersMalmgren/Knockout.BindingConventions/wiki/Template-convention

It works by conventions so if you have a member thats a model with name MyViewModel then it will look for a template named MyView

<div data-name="myMember"></div>

http://jsfiddle.net/xJL7u/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top