only publish when value changes

1,237 views
Skip to first unread message

Adrian Quark

unread,
Jan 20, 2011, 12:48:42 PM1/20/11
to KnockoutJS
I'm investigating replacing my own hacked-together data binding
library with Knockout.js and am frustrated by one apparently missing
feature: when writing a value to an observable, the observable should
only publish if the value actually changed. In other words, level
triggered rather than edge triggered. The main reason I need this
feature is history management. When the location hash changes, you
don't know which part of it changed, so you have to pessimistically
update all state associated with the history. For a very dynamic
application, this could mean rebuilding the entire page if you aren't
careful to detect what actually changed.

Of course, it's possible to explicitly check for changes in the
bindings themselves (or any other subscribers) and thereby prevent
unnecessary re-rendering. But then you have this "check if it
changed" logic spread all over the place instead of centralized in the
observable library. And I can't think of a reason you'd ever want to
recompute something based on a value which didn't actually change.

Did I miss this feature in the documentation or source code? And if
not, do you have any plans to add it?

Vin

unread,
Jan 20, 2011, 12:55:40 PM1/20/11
to KnockoutJS
I have been looking for a solution for the same problem, trying to do
Change/Delta tracking in order to send the deltas only to the server.

I am sure there is a neater, elegant solution out there, did you try
mapping plugin? I am trying to use it

fla...@gmail.com

unread,
Jan 23, 2011, 12:52:14 PM1/23/11
to KnockoutJS
Hi folks

There are two main reasons why KO doesn't always suppress
notifications of repeated values:

1. Sometimes you would want to know about them, for example if you
want to count the number of times data has been received from an Ajax
call, regardless of whether the data is the same as on the previous
call
2. There's no general way to know whether you consider two objects
to be the same or not

If the values you're writing are simple primitives, like strings, we
can of course compare values and say whether or not the new value is
different. But observables can hold arbitrary object graphs, so we
don't know whether you've mutated some property deep in the graph or
not (if we just do a reference comparison of the object itself, it
would appear to be unchanged even if a subproperty has changed), and
in any case you might have arbitrary business rules about whether or
not objects are considered the same (maybe you want to compare by some
sort of primary key rather than by all property values).

Despite all that, if you write your own logic to define whether or not
two values are equal (which can be as simple as "a == b" if you're
only dealing with primitives), then it's definitely possible, and
quite easy, to achieve what you want. See the example at http://jsfiddle.net/Ejdh6/3/
- when you click the A/B buttons, it only notifies when you switch
values, and ignores repeated clicks on the same value. The mechanism
I've used is an "interceptor" function that controls whether or not
writes to an observable are accepted. My logic is to ignore writes
that equal the observable's existing value, where "equal" means "equal
as primitives" since I'm only dealing with string values.

Perhaps ko.observable should natively have an "interceptWrite"
callback that you can use for this. For now it's simple to add that
functionality yourself by modifying the Function prototype as my
jsFiddle example shows.

Hope this helps
Steve

abp

unread,
Jan 28, 2011, 8:32:01 PM1/28/11
to KnockoutJS
Hi Steve,

i respect your opinion on this topic, but since i need this
functionality very often, even in writable dependentObservables, i'm
implementing this feauture right now.

I hope to get the pull request for ko and probably basic support in
ko.mapping ready today, by evening.

The concers you raised will be addressed like follows:
>   1. Sometimes you would want to know about them, for example if you
> want to count the number of times data has been received from an Ajax
> call, regardless of whether the data is the same as on the previous
> call
Because of that and backwards compatibility checks for value changes
are disabled by default.

>   2. There's no general way to know whether you consider two objects
> to be the same or not
Thats why it supports a true/false option value for primitives and a
comparator function for arbitrary objects.
We could provide a deep comparator in the future too for ease of use.

The signature of ko.observable changes, whilst dependentObservables
stay as they are, despite a new option:
ko.observable("", {checkValueChanged:true});

Your interceptor with change detection:
Function.prototype.intercept = function(callback, checkValueChanged)
{
var underlyingObservable = this;
return ko.dependentObservable({
read: underlyingObservable,
write: function(value)
{ callback.call(underlyingObservable, value) },
checkValueChanged: checkValueChanged
});
};
ko.observable().intercept(function(value) { /* crazy stuff goes here
*/ }, true);

I hope you see some advantages, too. The mapping plugin can support
the new change checks and since i use a lot of interceptors i have the
checks cluttered all over my code base.

I'm looking forward to get some feedback and a code review when its
pulled. :)

On 23 Jan., 18:52, "st...@codeville.net" <fla...@gmail.com> wrote:
> Hi folks
>
> There are two main reasons why KO doesn't always suppress
> notifications of repeated values:
>
>   1. Sometimes you would want to know about them, for example if you
> want to count the number of times data has been received from an Ajax
> call, regardless of whether the data is the same as on the previous
> call
>   2. There's no general way to know whether you consider two objects
> to be the same or not
>
> If the values you're writing are simple primitives, like strings, we
> can of course compare values and say whether or not the new value is
> different. But observables can hold arbitrary object graphs, so we
> don't know whether you've mutated some property deep in the graph or
> not (if we just do a reference comparison of the object itself, it
> would appear to be unchanged even if a subproperty has changed), and
> in any case you might have arbitrary business rules about whether or
> not objects are considered the same (maybe you want to compare by some
> sort of primary key rather than by all property values).
>
> Despite all that, if you write your own logic to define whether or not
> two values are equal (which can be as simple as "a == b" if you're
> only dealing with primitives), then it's definitely possible, and
> quite easy, to achieve what you want. See the example athttp://jsfiddle.net/Ejdh6/3/
> - when you click the A/B buttons, it only notifies when you switch
> values, and ignores repeated clicks on the same value. The mechanism
> I've used is an "interceptor" function that controls whether or not
> writes to an observable are accepted. My logic is to ignore writes
> that equal the observable's existing value, where "equal" means "equal
> as primitives" since I'm only dealing with string values.
>
> Perhaps ko.observable should natively have an "interceptWrite"
> callback that you can use for this. For now it's simple to add that
> functionality yourself by modifying the Function prototype as my
> jsFiddle example shows.
>
> Hope this helps
> Steve
>
> On Jan 20, 5:55 pm, Vin <kgvina...@gmail.com> wrote:
>
>
>
>
>
>
>
> > I have been looking for a solution for the same problem, trying to do
> >Change/Deltatrackingin order to send the deltas only to the server.

abp

unread,
Jan 29, 2011, 2:27:38 AM1/29/11
to KnockoutJS
Any feedback would be much appreciated:
https://github.com/SteveSanderson/knockout/pull/42
Reply all
Reply to author
Forward
0 new messages