Reputation: 169
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
Reputation: 1075755
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>
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