Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extended syntax for passing parameters to KnockoutJS templates

Tags:

knockout.js

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

like image 548
Gene Golovchinsky Avatar asked Jan 09 '12 18:01

Gene Golovchinsky


1 Answers

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.

like image 111
RP Niemeyer Avatar answered Sep 23 '22 07:09

RP Niemeyer