7
votes

I have come across several situations in which I have to code my template call like this:

<!-- ko template: {name: 'paginatedList', 
                   data: {listContainer: paginatedResults, itemTemplate: $parent.template}} --> 
<!-- /ko -->

because in addition to my data object (e.g., paginatedResults), I need to pass in supplemental information such as the name of the template to use in rendering items. As another example, when rendering an item in a list, I might need to know the current settings of a filter that is stored in some context logically far-removed from the item in a list.

Rather than having to contort my data model (in the above example, replacing paginatedResults with {listContainer: paginatedResults, itemTemplate: $parent.template}}, it would be nice to have a bit of syntax on templates that allows me to pass around (and acccrue as templates are evaluated hierarchically) some extra context that a deeply-nested template might need when rendering itself.

I guess what I wondering about is the feasibility of adding an extra (optional) parameter to the ko.bindingHandlers functions (init and update) to make them look like this:

function (element, 
          valueAccessor, 
          allBindingsAccessor, 
          viewModel, 
          bindingContext, 
          context)

When coding the above example, I ought to be able to say something like

<!-- ko template: {name: 'paginatedList', 
                   data: paginatedResults, 
                   context: {itemTemplate: $parent.template}} --> 
<!-- /ko -->

or better yet,

<!-- ko template: {name: 'paginatedList', 
                   data: paginatedResults, 
                   itemTemplate: $parent.template} --> 
<!-- /ko -->

and have itemTemplate become a variable I can refer to in nested templates and data-bind attributes.

Does this make sense? I don't have a good understanding of how hard this would be to implement. I guess one thing to worry about are name collisions, but some naming conventions might circumvent that.

Gene

1

1 Answers

9
votes

Without changes to Knockout core, I think that the best that you could do is a wrapper binding that creates a structure similar to what you currently are passing.

Binding might look like:

ko.bindingHandlers.templateWithContext = {
    init: ko.bindingHandlers.template.init,
    update: function(element, valueAccessor, allBindings, data, context) {
        var options = ko.utils.unwrapObservable(valueAccessor());

        ko.utils.extend(context, options.context);

        return ko.bindingHandlers.template.update.apply(this, arguments);
    } 
};   

You would call it like:

<div data-bind="templateWithContext: { name: 'itemsTmpl', data: items, context: { title: 'First' } }"></div>

Here is a sample: http://jsfiddle.net/rniemeyer/QNvFn/

It looks like you are using native templates, but if you are using jQuery Templates still, then it did include a templateOptions feature that used the options feature of jQuery Templates to pass context data. This is not available in the native templates. The general recommendation now is to use the $root, $parent, and $parents variables to access the information or to pass an object as data in the way that you described in your post.