How can I bind a ko.observableArray of strings?
Asked Answered
P

2

8

I'm trying to bind a ko.observableArray of strings to a template, but I'm unable to get the template to pick up changes in the strings inside the array.

If I bind a set of objects instead of a set of strings, I get updates to the JSON, but they don't trigger anything until I actually change the first, non-array value. I'd prefer to find an array of strings, however, as I will be able to post the data model directly back to the server without any post-processing.

How can I get the updates to my arrays of strings to trigger, and how can I ensure that they correctly trigger changes without having to update a non-array value?

If it's not possible to bind to an observable array of strings, how I can get the events to trigger when updating the objects inside of the observable array?

See the example here: http://jsfiddle.net/gcEHC/2/

In this example, array3's data will be reflected in the model when value is changed, but changes to array1 and array2's data will never show up.

JS:

var ViewModel = function() {
    this.value = ko.observable("hi")
    this.array1 = ko.observableArray(["hi", "there"]);
    this.array2 = ko.observableArray([ko.observable("hi"), ko.observable("there")]);
    this.array3 = ko.observableArray([{ data: "hi" }, { data: "there" }]);
};

ko.applyBindings(new ViewModel());

HTML:

<div class='liveExample'>   
    <p><input data-bind='value: value' /></p> 
    <div data-bind="foreach: array1">
        <p><input data-bind='value: $data' /></p> 
    </div>
    <div data-bind="foreach: array2">
        <p><input data-bind='value: $data' /></p> 
    </div>
    <div data-bind="foreach: array3">
        <p><input data-bind='value: data' /></p> 
    </div>
</div>

<pre data-bind="text: ko.toJSON($data)"></pre>
Precocity answered 1/4, 2013 at 16:24 Comment(0)
C
5

From the docs:

Simply putting an object into an observableArray doesn’t make all of that object’s properties themselves observable. Of course, you can make those properties observable if you wish, but that’s an independent choice. An observableArray just tracks which objects it holds, and notifies listeners when objects are added or removed.

You need to create an object that has an observable property (of your string). Then make an observableArray of those objects.

For example, here's an example that updates the property of an object 2 seconds after the bindings are applied:

var item = function(text) {
    var self = this;

    self.Name = ko.observable(text);
}

var vm = {
    items: ko.observableArray([
        new item('one'),
        new item('two'),
        new item('three')
        ])
}

ko.applyBindings(vm);
setTimeout(function() {
    vm.items()[1].Name('updated!');
}, 2000);

Here's a complete, runnable sample: http://jsfiddle.net/psteele/z6gbM/

Chace answered 1/4, 2013 at 16:38 Comment(5)
when I created an observable array of observable strings, it still doesn't work. See array2 in the jsfiddle linked above. Note that the changes to the observable array of strings are never picked up - no matter if the contents are observable or not.Precocity
Hi Patrick, thanks for your updated code - but the question really is why does't this work: this.array2 = ko.observableArray([ko.observable("hi"), ko.observable("there")]);. Nothing in the documentation suggests that it shouldn't work.Precocity
In array2 you're creating an observableArray on two javascript "String" objects ("hi" and "there"). Since there's no properties on javascript's string type that are ko.observable, changes to it won't be caught by knockout.Chace
Sorry, but am I not binding on two observable strings in array2, rather than two strings? Isn't this the same as binding to the observable string directly? And in that case, the binding does update in realtime.Precocity
I'm going to accept this answer as-is and open up another question about observable arrays of observables.Precocity
M
10

In KO 3, if you point to $rawData in the binding, the array2 example works as expected.

Mciver answered 16/7, 2014 at 18:53 Comment(1)
Thanks, forgot about $rawData ; had the same problem, now it works if passing directly to the array a string observable.Sidesaddle
C
5

From the docs:

Simply putting an object into an observableArray doesn’t make all of that object’s properties themselves observable. Of course, you can make those properties observable if you wish, but that’s an independent choice. An observableArray just tracks which objects it holds, and notifies listeners when objects are added or removed.

You need to create an object that has an observable property (of your string). Then make an observableArray of those objects.

For example, here's an example that updates the property of an object 2 seconds after the bindings are applied:

var item = function(text) {
    var self = this;

    self.Name = ko.observable(text);
}

var vm = {
    items: ko.observableArray([
        new item('one'),
        new item('two'),
        new item('three')
        ])
}

ko.applyBindings(vm);
setTimeout(function() {
    vm.items()[1].Name('updated!');
}, 2000);

Here's a complete, runnable sample: http://jsfiddle.net/psteele/z6gbM/

Chace answered 1/4, 2013 at 16:38 Comment(5)
when I created an observable array of observable strings, it still doesn't work. See array2 in the jsfiddle linked above. Note that the changes to the observable array of strings are never picked up - no matter if the contents are observable or not.Precocity
Hi Patrick, thanks for your updated code - but the question really is why does't this work: this.array2 = ko.observableArray([ko.observable("hi"), ko.observable("there")]);. Nothing in the documentation suggests that it shouldn't work.Precocity
In array2 you're creating an observableArray on two javascript "String" objects ("hi" and "there"). Since there's no properties on javascript's string type that are ko.observable, changes to it won't be caught by knockout.Chace
Sorry, but am I not binding on two observable strings in array2, rather than two strings? Isn't this the same as binding to the observable string directly? And in that case, the binding does update in realtime.Precocity
I'm going to accept this answer as-is and open up another question about observable arrays of observables.Precocity

© 2022 - 2024 — McMap. All rights reserved.