Reputation: 13
I'm a beginner to Knockoutjs. While working with collections it seems to me binding does not work (text is shown but it's not updated after value updating). As example the following code:
<h2>People</h2>
<ul data-bind="foreach: people">
<li>
<div>
<span data-bind="text: name"> </span> has <span data-bind='text: children().length'> </span> children:
<a href='#' data-bind='click: addChild '>Add child</a>
<span class='renderTime' data-bind='visible: $root.showRenderTimes'>
(person rendered at <span data-bind='text: new Date().getSeconds()' > </span>)
</span>
</div>
<ul data-bind="foreach: children">
<li>
<input type="text" data-bind="value: $data">
<span data-bind="text: $data"> </span>
<span class='renderTime' data-bind='visible: $root.showRenderTimes'>
(child rendered at <span data-bind='text: new Date().getSeconds()' > </span>)
</span>
</li>
</ul>
</li>
</ul>
<label><input data-bind='checked: showRenderTimes' type='checkbox' /> Show render times</label>
It's a code from example on the official Knockout.js site http://knockoutjs.com/examples/collections.html The source js code is this:
// Define a "Person" class that tracks its own name and children, and has a method to add a new child
var Person = function(name, children) {
this.name = name;
this.children = ko.observableArray(children);
this.addChild = function() {
this.children.push("New child");
}.bind(this);
}
// The view model is an abstract description of the state of the UI, but without any knowledge of the UI technology (HTML)
var viewModel = {
people: [
new Person("Annabelle", ["Arnie", "Anders", "Apple"]),
new Person("Bertie", ["Boutros-Boutros", "Brianna", "Barbie", "Bee-bop"]),
new Person("Charles", ["Cayenne", "Cleopatra"])
],
showRenderTimes: ko.observable(false)
};
ko.applyBindings(viewModel);
I've just made one change - added
<input type="text" data-bind="value: $data">
Everything is fine displayed. But text is not updated after value changing. It seems that binding does not work. I faced with the same problem in a real project as well. It is confused me. What's going wrong? Thanks in advance.
Upvotes: 0
Views: 64
Reputation: 63719
The example you took isn't particularly well suited to make a two-way binding work, but let's try anyways. Note that children
is an observable array
(so additions/removals are noticed) but the items inside it are plain strings, not observables. For a two-way binding to work you'd need that.
In addition, a "child" is just a plain string, further complicating things.
I suggest editing the example a bit to make sure children are mapped to new Person(...)
instances and have their name
property be an ko.observable(...)
. The addChild
function should then also create new Person(...)
instances.
See this example:
// Define a "Person" class that tracks its own name and children, and has a method to add a new child
var Person = function(name, children) {
this.name = ko.observable(name);
this.children = ko.observableArray(children.map(c => new Person(c, [])));
this.addChild = function() {
this.children.push(new Person("New child", []));
}.bind(this);
}
// The view model is an abstract description of the state of the UI, but without any knowledge of the UI technology (HTML)
var viewModel = {
people: [
new Person("Annabelle", ["Arnie", "Anders", "Apple"]),
new Person("Bertie", ["Boutros-Boutros", "Brianna", "Barbie", "Bee-bop"]),
new Person("Charles", ["Cayenne", "Cleopatra"])
],
showRenderTimes: ko.observable(false)
};
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<h2>People</h2>
<ul data-bind="foreach: people">
<li>
<div>
<span data-bind="text: name"> </span> has <span data-bind='text: children().length'> </span> children:
<a href='#' data-bind='click: addChild '>Add child</a>
<span class='renderTime' data-bind='visible: $root.showRenderTimes'>
(person rendered at <span data-bind='text: new Date().getSeconds()' > </span>)
</span>
</div>
<ul data-bind="foreach: children">
<li>
<input type="text" data-bind="value: name">
<span data-bind="text: name"> </span>
<span class='renderTime' data-bind='visible: $root.showRenderTimes'>
(child rendered at <span data-bind='text: new Date().getSeconds()' > </span>)
</span>
</li>
</ul>
</li>
</ul>
<label><input data-bind='checked: showRenderTimes' type='checkbox' /> Show render times</label>
Upvotes: 1