Search code examples
knockout.jsvisibleobservable

Why does my ko computed observable not update


I am trying to use the visible knockout binding and index within a foreach to implement line by line editing. This is done by creating a display and modify div for each line and using visible binding to select between them. The problem is that the observable does not track between the modify and display divs.

I created this fiddle http://jsfiddle.net/rscidmore/YrsCj/57/

To see the problem, click modify on a line, edit the value and click save. You can see that you now have two values for the same observable.

Javascript:
var myViewModel = {
    vals: ko.observableArray([
        {label: 'first',  item: 'one'},
        {label: 'second', item: 'two'},
        {label: 'third',  item: 'three'}
    ]),
    idx: ko.observable(-1)
};

ko.applyBindings(myViewModel);



HTML:
<body class='ui-widget'>
<div class='container'>
   <!-- ko foreach: vals -->
     <div class='label' data-bind="visible: $root.idx() == $index()">
       <span class='lbl' data-bind="text: label"></span>:
       <input type='text' data-bind="value: item">
       <input type='button' value="save" data-bind="click: function()
         { $root.idx(-1);}" />
     </div>
     <div class='label' data-bind="visible: $root.idx() != $index()">
       <span class='lbl' data-bind="text: label"></span>:
       <span data-bind="text: item"></span>
       <input type='button' value="modify" data-bind="click: function()
         { $root.idx($index());}" />
     </div>
   <!-- /ko -->
</div>
</body>

Solution

  • The ko.observableArray only tracks item addition and removal, and it won't track if one its items have changed.

    Form the documenation:

    Key point: An observableArray tracks which objects are in the array, not the state of those objects

    So you need to have observable properties on the items in order to track their changes:

    vals: ko.observableArray([
            {label: 'first',  item: ko.observable('one')}, 
            {label: 'second', item: ko.observable('two')}, 
            {label: 'third',  item: ko.observable('three')}
        ]),
    

    Demo fiddle.