HPrakash
HPrakash

Reputation: 21

KnockoutJs array binding

My knockoutjs code not working as I expected. Kindly see the code at http://jsfiddle.net/x0vdo9kk/1/.

// 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("Charles", ["Cayenne", "Cleopatra"])
    ],
    outcode: ko.observable(),
    showcode: function() {
        this.outcode(this.people[0].children())
    }
};

ko.applyBindings(viewModel);

And html is

<div class='liveExample'> 

    <h2>People</h2>
    <ul data-bind="foreach: people">
        <li>
            <div>
                <span data-bind="text: name"> </span> has <span data-bind='text: children().length'>&nbsp;</span> children:
                <a href='#' data-bind='click: addChild '>Add child</a>

            </div>
            <ul data-bind="foreach: children">
                <li>
                    <input data-bind="value: $data" />
                </li>
            </ul>
        </li>
    </ul>
    <div data-bind="text: outcode"></div>
    <button data-bind='click: showcode'>out</button> 
</div>

Before pressing the "out" button change textbox values.

Then press the "out" button. The result is array init values, not the new values.

Kindly tell me what am I missing.

Upvotes: 0

Views: 222

Answers (2)

Dmitriy Krupin
Dmitriy Krupin

Reputation: 591

An observableArray tracks which objects are in the array, not the state of those objects see documentation, so you have to make observable array of obsrevables.

var Person = function(name, children) {
this.name = name;
this.children = ko.observableArray(ko.utils.arrayMap(children,function(child){
    return ko.observable(child);
}));

this.addChild = function() {
    this.children.push(ko.observable("New child"));
}.bind(this);}

Also in foreach binding you may use $rawData for two-way binding if you dont wont to make object

<ul data-bind="foreach: children">
<li>
    <input data-bind="value: $rawData" />
</li>

Updated JsFiddle demo

Upvotes: 5

super cool
super cool

Reputation: 6045

Well i made some changes to your existing code as you are directly binding plain array which is inside observableArray to view via $data .

View Model:

//used to make plan array items observable's
function Name(val) {
    this.name = ko.observable(val);
}

var Person = function (name, children) {
    this.name = ko.observable(name);
    this.children = ko.observableArray();
    //mapping to make observable s 
    var oList = ko.utils.arrayMap(children, function (item) {
        return new Name(item);
    });
    this.children(oList);

    this.addChild = function () {
        this.children.push(new Name("New child"));
    }.bind(this);
}

var viewModel = function () {
    var self = this;
    self.people = ko.observableArray();
    self.people.push(new Person("Annabelle", ["Arnie", "Anders", "Apple"]), new Person("Charles", ["Cayenne", "Cleopatra"]));
    self.outcode = ko.observable("");
    self.showcode = function () {
       //reverse mapping returning plane array using `()` on observables
        var output = ko.utils.arrayMap(self.people()[0].children(), function (item) {
            return item.name();
        });
        self.outcode(output);
    }
};

ko.applyBindings(new viewModel());

View : (changed part of view)

<ul data-bind="foreach: children">
            <li>
                <input data-bind="value:name" />
            </li>
        </ul>

Working fiddle here

Upvotes: 2

Related Questions