Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tracking changes - observable element in observableArray

Tags:

knockout.js

I need to edit an array of integers in a web application, using Javascript and Knockout.js. This array will be bound to text boxes, and whenever the value of any text box is changed, the array is updated. And when it gets updated, the sum of the elements is calculated.

This is my first try: http://jsfiddle.net/ZLs2A/0/. Its not working (the sum value is not update when typing a new value for any element). That's when I realized observableArray will only trigger the sum function after inserting or removing items.

<h4>Numbers</h4>
<button data-bind="click: add">Add</button>
<ul data-bind="foreach: numbers">
    <li>
        <input data-bind="value: $data"></input>
    </li>
</ul>
<span data-bind="text: sum"></span>

function MyViewModel() {
    var self = this;

    self.numbers = ko.observableArray([
        1,
        2,
        3
    ]);

    self.sum = ko.computed(function() {
        var items = self.numbers();
        var total = 0;
        for (var i = 0; i < items.length; i++) {
            total += parseInt(items[i]);
        }
        return total;
    });

    self.add = function() {
        var lastIndex = self.numbers().length - 1;
        var lastValue = self.numbers()[lastIndex];
        self.numbers.push(lastValue + 1);
    }
}

ko.applyBindings(new MyViewModel());

My next attempt was to make every element in the numbers array an observable (http://jsfiddle.net/ZLs2A/1/). It did not work again.

self.numbers = ko.observableArray([
    ko.observable(1),
    ko.observable(2),
    ko.observable(3)
]);

My last try was creating a new class (ArrayItem) to hold the value of the element inside a observable property. This time it worked! (http://jsfiddle.net/ZLs2A/3/)

<h4>Numbers</h4>
<button data-bind="click: add">Add</button>
<ul data-bind="foreach: numbers">
    <li>
        <input data-bind="value: value"></input>
    </li>
</ul>
<span data-bind="text: sum"></span>

function MyViewModel() {
    var self = this;

    self.numbers = ko.observableArray([
        new ArrayItem(1),
        new ArrayItem(2),
        new ArrayItem(3)
    ]);

    self.sum = ko.computed(function() {
        var items = self.numbers();
        var total = 0;
        for (var i = 0; i < items.length; i++) {
            total += parseInt(items[i].value());
        }
        return total;
    });

    self.add = function() {
        var lastIndex = self.numbers().length - 1;
        var lastItem = self.numbers()[lastIndex];
        var newValue = parseInt(lastItem.value()) + 1;
        self.numbers.push(new ArrayItem(newValue));
    }
}

function ArrayItem(value){
    var self = this;
    self.value = ko.observable(value);
}

ko.applyBindings(new MyViewModel());

However, I didn't like to have to create this new class ArrayItem. Is there any way to get rid of it, making the example work just having a observableArray of observable elements (like my second attempt)?

like image 580
Fabio Avatar asked May 02 '13 18:05

Fabio


People also ask

How do you update items in observableArray knockout?

You should look at defining the object structure for each element of your array and then add elements of that type to your observable array. Once this is done, you will be able to do something like item. productQuantity(20) and the UI will update itself immediately. EDIT Added the function provided by the OP :).

How do I sort knockout observable array?

Description. The KnockoutJS Observable sort() method sorts all items in the array. By default, items are sorted in an ascending order. For sorting an array in a descending order, use reverse() method on sorted array.

How do you clear observable in knockout?

It can be like this. var model = { "xId": ko. observable(0), "xName": ko. observable(null), "Type": ko.

How do you remove data from an observable array?

shift() — Removes the first value from the array and returns it. reverse() — Reverses the order of the array and returns the observableArray (not the underlying array). sort() — Sorts the array contents and returns the observableArray .


Video Answer


1 Answers

Knockout will not track the value, but as a workaround you can use event keypress or change and update values yourself. See fiddle: http://jsfiddle.net/tkirda/ZLs2A/4/

function MyViewModel() {
    var self = this;

    self.numbers = ko.observableArray([
        1,
        2,
        3
    ]);

    self.onChange = function(val, e){
        var el = e.srcElement;
        var newVal = parseInt(el.value);
        var index = parseInt(el.getAttribute('data-index'));
        self.numbers()[index] = newVal;
        self.updateSum();
    }

    self.sum = ko.observable(0);

    self.updateSum = function() {
        var items = self.numbers();
        var total = 0;
        for (var i = 0; i < items.length; i++) {
            total += parseInt(items[i]);
        }
        self.sum(total);
    };

    self.updateSum();

    self.add = function() {
        var lastIndex = self.numbers().length - 1;
        var lastValue = self.numbers()[lastIndex];
        self.numbers.push(lastValue + 1);
    }
}

ko.applyBindings(new MyViewModel());
like image 66
Tomas Kirda Avatar answered Oct 23 '22 01:10

Tomas Kirda