user3213045
user3213045

Reputation: 169

knockout JS inner and outer bindings

I would like to include an inner element in an outer element using knockout, is this in any way possible?

HTML:

<div id='id1'>
    <span data-bind="text: outerText()"></span>
    <div id='id2'>
        <span data-bind="text: innerText()"></span>
    </div>
</div>

JavaScript:

var outerModel = function() {
    this.outerText = ko.observable("Outer Model - Outer Text");
};
ko.applyBindings(new outerModel(), document.getElementById('id1'));

var innerModel = function() {
    this.innerText = ko.observable("Inner Model - Inner Text");
};
ko.applyBindings(new innerModel(), document.getElementById('id2'));

This gives me an error:

ReferenceError: Unable to process binding "text: function(){return innerText() }"
Message: 'innerText' is undefined

I understand the error as the outer model doesn't contain the innertext and therefore the thing crashes.

My questions is if there is a proper/better/correct way of having an inner element and getting it to work in knockout.

Note: I do not want the innerModel to be a member/child of the outerModel as they are just in this HTML layout for layout purposes but aren't necessarily related.

Any help appreciated.

Thanks

Upvotes: 2

Views: 186

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1075755

  1. Usually your best bet there is to make the inner stuff a property of your outer stuff and then just bind normally (possibly with with). E.g.:

    var innerModel = function() {
        this.innerText = ko.observable("Inner Model - Inner Text");
    };
    var outerModel = function() {
        this.outerText = ko.observable("Outer Model - Outer Text");
        this.inner = ko.observable(new innerModel());
    };
    ko.applyBindings(new outerModel(), document.getElementById('id1'));
    

    ...and then:

    <div id='id1'>
        <span data-bind="text: outerText()"></span>
        <div id='id2' data-bind="with: inner">
            <span data-bind="text: innerText()"></span>
        </div>
    </div>
    

    Example:

        var innerModel = function() {
            this.innerText = ko.observable("Inner Model - Inner Text");
        };
        var outerModel = function() {
            this.outerText = ko.observable("Outer Model - Outer Text");
            this.inner = ko.observable(new innerModel());
        };
        ko.applyBindings(new outerModel(), document.getElementById('id1'));
        <div id='id1'>
            <span data-bind="text: outerText()"></span>
            <div id='id2' data-bind="with: inner">
                <span data-bind="text: innerText()"></span>
            </div>
        </div>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

  2. But in cases where that's not possible, you can add a new binding to KO that says "don't bind within this element" as described here:

    ko.bindingHandlers.stopBinding = {
        init: function () {
            return { controlsDescendantBindings: true };
        }
    };
    

    Usage:

    <div id='id1'>
        <span data-bind="text: outerText()"></span>
        <div data-bind="stopBinding: true">
            <div id='id2'>
                <span data-bind="text: innerText()"></span>
            </div>
        </div>
    </div>
    

    ...and then do the two applyBindings in your question. (Note that I added a div around your id2 div. If you want to use a "virtual element" instead, add this line after the binding handler:

    ko.virtualElements.allowedBindings.stopBinding = true;
    

    ...to enable using it with virtual elements.)

    Example:

        // Somewhere where you'll only do it once
        ko.bindingHandlers.stopBinding = {
            init: function () {
                return { controlsDescendantBindings: true };
            }
        };
    
        // Then:
        var outerModel = function() {
            this.outerText = ko.observable("Outer Model - Outer Text");
        };
        var innerModel = function() {
            this.innerText = ko.observable("Inner Model - Inner Text");
        };
        ko.applyBindings(new outerModel(), document.getElementById('id1'));
        ko.applyBindings(new innerModel(), document.getElementById('id2'));
        <div id='id1'>
            <span data-bind="text: outerText()"></span>
            <div data-bind="stopBinding: true">
                <div id='id2'>
                    <span data-bind="text: innerText()"></span>
                </div>
            </div>
        </div>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

Upvotes: 2

Related Questions