performance issue with select elements (and binding in general)

272 views
Skip to first unread message

Michael Best

unread,
Nov 29, 2011, 8:18:06 PM11/29/11
to knock...@googlegroups.com

I just posted a message about a bug with select elements. While researching the bug, I realized that Knockout deletes all the options and re-adds them each time you make a selection in a select element. That’s because whenever you select an item, it changes the value of the variable bound to ‘value’ and triggers an update call on all the other bindings on the select, including ‘options’. This seems to be an inherent flaw in the design of Knockout that an update for one binding will update all the others even it’s not necessary.

 

I think to fix this would require each binding being set up as an individual dependent observable for each binding instead of the current method of having a single one for all bindings.

 

What do you think?

 

Thanks,

Michael

Peter S

unread,
Nov 30, 2011, 4:34:16 AM11/30/11
to KnockoutJS
I flagged a similar issue to yours here: https://github.com/SteveSanderson/knockout/issues/61
and concluded the cause was the same.

It would be nice if ko was a bit smarter about how it executes these
bindings, performance with a lot of select elements on screen can be
quite painful. I don't have the technical nous to make any guesses as
to how to do it though unfortunately.

jschr

unread,
Nov 30, 2011, 9:19:48 AM11/30/11
to knock...@googlegroups.com
I've had the same issue in the past. I came across a solution that I was able to implement on my custom options binding to help with this problem. Basically, I created a custom options binding that only has an init function. In this init function you create an anonymous dependent observable to run your update logic. This causes the options binding update to not be run everytime an update is fired from another binding. 

ko.bindingHandlers.myOptionsBinding = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        ko.dependentObservable({
    read: function() {
                  //update logic goes here
            },
    disposeWhenNodeIsRemoved: element
});
    }
}


jschr

unread,
Nov 30, 2011, 9:24:20 AM11/30/11
to knock...@googlegroups.com
credit to rpn who just posted this fiddle in another thread which may help:  http://jsfiddle.net/rniemeyer/uKUfy/

rpn

unread,
Nov 30, 2011, 9:30:59 AM11/30/11
to knock...@googlegroups.com
This topic has been brought up before.  In some cases, bindings have dependencies between each other and running all of the bindings is appropriate (when options update, value needs to run to ensure that the value is still valid).  However, in many cases this behavior is not ideal.

I think that short-term (probably 1.3.1), the options/value issue will be addressed.   This would hopefully include both not needing to put the options binding before the value binding and helping with the performance issue.  

Likely we would have the options and value binding be aware of each other.  If the value binding is present, then the options binding would not do its work.  Then, in the value binding if we see that the options binding is present, we can execute the options logic in a separate dependentObservable that is created in the init function.  That way we won't depend on the order and the options/value bindings won't both execute every time one is triggered.

For custom bindings, you can already do this by putting your "update' logic in your own dependentObservable that you create in the init function.  Similar to this thread and what jschr is describing in this thread.


jschr

unread,
Nov 30, 2011, 9:44:25 AM11/30/11
to knock...@googlegroups.com
Here's how I believe you could apply the trick rpn mentioned to your own options binding:  http://jsfiddle.net/Y4UyV/4/

Hope that helps

jschr

unread,
Nov 30, 2011, 10:22:34 AM11/30/11
to knock...@googlegroups.com
edited slightly due to redundant unwrapping of the valueAccessor:  http://jsfiddle.net/Y4UyV/5/

Michael Best

unread,
Nov 30, 2011, 4:38:05 PM11/30/11
to KnockoutJS
I've created a generic custom binding, async, that can be used to make
any binding run out-of-sync with the other bindings. You can see it in
action here: http://jsfiddle.net/tems9/

This is useful here to separate the options and value bindings (value
cannot be in async because options needs to access it).

It can also be used, for example, if you have both template and
visible bindings on an element so that you can change the visibility
without rerunning the template.

Note that if your binding directly accesses an observable or calls a
function that accesses an observable, putting it async will not work
properly.

Michael Best

unread,
Nov 30, 2011, 4:49:17 PM11/30/11
to KnockoutJS
I've updated the jsfiddle to show how to make the value update if the
options for the select are updated:

http://jsfiddle.net/tems9/1/

In this update, I added a button that deletes the selected item. With
the async options, the delete happens, but the value isn't updated. So
I also added a 'dummy' binding that accesses the array so that any
update to the array will update the value also.

-- Michael

Reply all
Reply to author
Forward
0 new messages