Search code examples
knockout.jsknockout-templating

Referring to the data passed to the template using $root


I have a template:

<script id="segment-instructions" type="text/html">
    <div data-bind="foreach: Conjunctions">
        <!-- Deep hierarchy -->
            <p data-bind="visible: !$root.hasAnyValidExpression($data)" />             
    </div>
</script>

And the following markup where I use it:

<div class="tab-content" id="options-tabs">
    ...
    <div data-bind="template: { name: 'segment-instructions', data: Instructions }"></div>
</div>

This is how I apply the binding:

var instructionsModel = new SegmentInstructionsModel();
...

ko.applyBindings({
    GeneralOptions: generalOptionsModel,
    Instructions: instructionsModel
}, $('#options-tabs').get(0));

The problem is that $root in the template is resolved to the object passed to ko.applyBindings (i.e. the object with GeneralOptions and Instructions properties), not to the object specified in the template binding, which is an instance of my SegmentInstructionsModel class.

I could solve this in at least two ways, none of which I like:

  1. Keep using $root in the template, but traversing down to the actual view model rendered by the template

    <p data-bind="visible: !$root.Instructions.hasAnyValidExpression($data)" />
    

    I don't like this since the template should not worry about how the object above the actual view model is structured.

  2. Use the $parents array

    <p data-bind="visible: !$parents[$parents.length - 2].hasAnyValidExpression($data)" />
    

    I don't like this for obvious reasons (for one, it stops working once the actual template's view model is passed to ko.applyBinding)

Is there a way to keep using $root in a template and not worry about tight coupling of the template with the way its consumer supplies the data to it?


Solution

  • After all, using the $parents array looks like the way to go, only traversing from the bottom instead from the top (which is not reliable, see above). That is, in my case it would be something like

    <p data-bind="visible: !$parents[3].hasAnyValidExpression($data)" />