ClickThisNick ClickThisNick - 4 months ago 134
HTML Question

Knockout js foreach binding with expand/collapse

I've read a couple tutorials and tried for a couple hours trying to get this to work. My goal is to have multiple links that expand/collapse when they are clicked on. So far I have the following:

HTML:

<ul data-bind="foreach: items">
<a href="#" data-bind="click: $parent.toggle, text: $parent.linkLabel"></a>
<button data-bind="text: name, click: $parent.clickTask"></button>
<div data-bind="visible: $parent.expanded">
<input data-bind="value: name"></input>
</div>
</ul>


JS:

var viewModel = function() {
var self = this;

self.items = [{"name":"bruce","id":1},{"name":"greg","id":2}]

self.expanded = ko.observable(false);

self.linkLabel = ko.computed(function () {
return self.expanded() ? "collapse" : "expand";
}, self);

self.toggle = function (item) {
self.expanded(!self.expanded());
};
};

ko.applyBindings(new viewModel());


JSFiddle Here

I understand right now I have the expanded state on the parent which is why everything expands/collapses. How would I get each item to keep track of its own expand/collage state?

Answer

well you are creating a single dependency for all observable's by referring parent which is the issue here .

So you need to have independent dependency for each list item and make use of $data which refers current context . So trick here is creating a instance for each listitem

view:

<ul data-bind="foreach: items"> <a href="#" data-bind="click: toggle, text:linkLabel"></a> 
    <button data-bind="text:name"></button>
    <div data-bind="visible:expanded">
        <input data-bind="value:name"></input>
    </div>
</ul>

viewModel:

function Sample(item) {
    var self = this;
    self.name = ko.observable(item.name);
    self.id = ko.observable(item.id);
    self.expanded = ko.observable(false);
    self.toggle = function (item) {
        self.expanded(!self.expanded());
    };
    self.linkLabel = ko.computed(function () {
        return self.expanded() ? "collapse" : "expand";
    }, self);
}

var viewModel = function () {
    var self = this;

    var json = [{
        "name": "bruce",
        "id": 1
    }, {
        "name": "greg",
        "id": 2
    }]

    var data = ko.utils.arrayMap(json, function (item) {
        return new Sample(item); // making things independent here 
    });
    self.items = ko.observableArray(data);
};

ko.applyBindings(new viewModel());

working sample up here