Updating properties in items in observableArray does not propagate to the template/view

11,185 views
Skip to first unread message

hCon

unread,
Oct 13, 2010, 4:36:39 AM10/13/10
to KnockoutJS
Hi,

we are facing a problem with knockout that we did not expect.

Knockout seems to react only to changes in the observableArray itself,
like when you do slice, push, pop etc. But if we change a property
inside an item in the observableArray, nothing happens in the view.
The items in the array is updated by an ajax method that polls the
server every five seconds

What is the solution to this, please?

Александр Прокофьев

unread,
Oct 13, 2010, 5:18:09 AM10/13/10
to knock...@googlegroups.com
Hi.

I believe you should do this property observable itself to reflect its
value changes.

2010/10/13 hCon <lag...@gmail.com>:

Sincerely, Alexander Prokofyev.

dove

unread,
Oct 13, 2010, 5:21:52 AM10/13/10
to KnockoutJS
I've noticed the same and have been trying to figure what I'm doing
wrong.

I've an observable array that is successfully bound to a template,
similar to the gifts sample. Changes are reflected within the
viewModel and I can post this data.

However, like yourself, the template items that are also linked to
properties in the model that are changed do not update automatically.

Colum

dove

unread,
Oct 13, 2010, 5:37:37 AM10/13/10
to KnockoutJS
Alexander,

Is there a shorthand for making each property observable?

Can't see how this would be easily done using templates either.
Taking the sample for instance, what would be done:

<script type="text/html" id="giftRowTemplate">
<tr>
<td>Gift name: <input class="required" data-bind="value:
Title, uniqueName: true"/></td>
<td>Price: \$ <input class="required number" data-
bind="value: Price, uniqueName: true"/></td>
<td><a href="#" data-bind="click: function()
{ viewModel.removeGift($data) }">Delete</a></td>
<td><label data-bind="text: Title" /></td>
</tr>
</script>

<script type="text/javascript">
var initialData = <%= new
JavaScriptSerializer().Serialize(Model) %>;
var viewModel = {
gifts : ko.observableArray(initialData),

addGift: function () {
this.gifts.push({ Title: "", Price: "" });
},

removeGift: function (gift) {
this.gifts.remove(gift);
},

save: function() {
ko.utils.postJson(location.href, { gifts:
this.gifts });
}
};

ko.applyBindings(viewModel);
$("form").validate({ submitHandler: function()
{ viewModel.save() } });
</script>

Александр Прокофьев

unread,
Oct 13, 2010, 6:05:25 AM10/13/10
to knock...@googlegroups.com
In your case I would try executing this code before binding:

ko.utils.arrayForEach(viewModel.initialData(), function(item)
{
item.Title = ko.observable(item.Title);
item.Price = ko.observable(item.Price);
});

Hope Steve will suggest more smart (or universal) way.

2010/10/13 dove <co...@clissmann.net>:

--
С уважением                                        Александр Прокофьев.

dove

unread,
Oct 13, 2010, 6:05:59 AM10/13/10
to KnockoutJS
Alexander,

Thanks for the tip, that was exactly it.

I simply marked each item in the array as observable before passing
into the observableArray.

For anyone who's interested in the specifics to code above (this would
make the Title observable):

$(initialData).each(function(gift) {
this.Title = ko.observable(this.Title);
});

Still getting my head around knockout and templating in general but
like both very much, and much better than home brew versions I'd been
using before.

Colum

dove

unread,
Oct 13, 2010, 6:09:14 AM10/13/10
to KnockoutJS
Alexander,

Thanks for the second reply, I'd done the same but with jquery.

Like you say, a setting on observableArray might be nice, to make all
properties observable. Assuming it's not done for performance
reasons.

Colum

dove

unread,
Oct 13, 2010, 6:49:03 AM10/13/10
to KnockoutJS
Alexander,

With this, is your array linking affected? The properties which I
explicitly make observable are now not linked within the array.

Colum

fla...@gmail.com

unread,
Oct 13, 2010, 8:22:11 AM10/13/10
to KnockoutJS
> Knockout seems to react only to changes in the observableArray itself,
> like when you do slice, push, pop etc

Yes, Alexander is right - observableArray is a way of tracking which
items are in a collection, not a way of making everything referenced
through the whole object graph observable. There's some documentation
about observable arrays at http://github.com/SteveSanderson/knockout/wiki/observable-arrays.

JavaScript doesn't fire any sort of event when arbitrary properties
change, so there's no way KO can notice what's changed inside an
object unless you've made that specific property observable.
Observable arrays don't try to scan the whole object graph to convert
everything to observables because in many cases you wouldn't want to
do such a dramatic thing, and you might have references to things that
get confused if you interfere with their properties (for example, your
object graph might include a reference to a DOM element or the
"window" object).

However, in the case where you do want to do that, some utility code
like Alexander's will work. You could try to generalise it but it's
probably clearer and more robust as it is.

> With this, is your array linking affected? The properties which I
> explicitly make observable are now not linked within the array.

Could you clarify what you mean? Ideally if you could post a code
example it would be easiest to suggest what's happening.

Regards!
Steve

dove

unread,
Oct 13, 2010, 8:30:54 AM10/13/10
to KnockoutJS
Steve.

Thanks for getting back so soon. I'm trying to get this working with
radio lists but I can reproduce within your gifts sample, as given
below. I've tried making the viewModel Title observable before the
array (commented out in example) and tried using Alexander's after the
observableArray is called but before binding as below.

The linking works on the page but the viewModel.Title seems to be
cleared. I see it upon posting but thinking it happens before then.

Colum

code:


<script type="text/html" id="giftRowTemplate">
<tr>
<td>Gift name: <input class="required" data-bind="value:
Title, uniqueName: true"/></td>
<td>Price: \$ <input class="required number" data-
bind="value: Price, uniqueName: true"/></td>
<td><a href="#" data-bind="click: function()
{ viewModel.removeGift($data) }">Delete</a></td>
<td><label data-bind="text: Title" /></td>
</tr>
</script>

<script type="text/javascript">
var initialData = <%= new
JavaScriptSerializer().Serialize(Model) %>;

// $(initialData).each(function(gift) {
// this.Title = ko.observable(this.Title);
// });

var viewModel = {
gifts : ko.observableArray(initialData),

addGift: function () {
this.gifts.push({ Title: "", Price: "" });
},

removeGift: function (gift) {
this.gifts.remove(gift);
},

save: function() {
ko.utils.postJson(location.href, { gifts:
this.gifts });
}
};

ko.utils.arrayForEach(viewModel.gifts(), function(item)
{
item.Title = ko.observable(item.Title);

Kim Ruben Vatnehagen

unread,
Oct 13, 2010, 9:19:13 AM10/13/10
to KnockoutJS
There's probably alot of people using the observableArray to store
full objects, and then have som ajax function requesting updates for
the initial dataset.
A sort of generic solutions would be to add something like:

result.update = function (newobject, oldobject) {
var underlyingArray = result();
var i = ko.utils.arrayIndexOf(underlyingArray, oldobject);
if (i != -1) {
underlyingArray[i] = newobject;
result.valueHasMutated();
}
};

to the observableArray function of ko.

This would update the object without having to explicitly set
observable properties for each object in the observable array.
I haven't had time to investigate any drawbacks or pitfalls of this
yet.

Regards
Kim Ruben

On Oct 13, 2:22 pm, "st...@codeville.net" <fla...@gmail.com> wrote:
> > Knockout seems to react only to changes in the observableArray itself,
> > like when you do slice, push, pop etc
>
> Yes, Alexander is right - observableArray is a way of tracking which
> items are in a collection, not a way of making everything referenced
> through the whole object graph observable. There's some documentation
> about observable arrays athttp://github.com/SteveSanderson/knockout/wiki/observable-arrays.

oha...@gmail.com

unread,
Aug 15, 2014, 4:44:24 AM8/15/14
to knock...@googlegroups.com, kir...@gmail.com
Resurrecting this for the benefit of anyone who get here from a web search (like I did).
This is a bad idea for large arrays - you don't wan't to call valueHasMutated for the entire array where only one object changed.
The method offered by Alexander is generally a better idea.

Best,
Ohad
Reply all
Reply to author
Forward
0 new messages