How to get the observable array which a select is bound to

335 views
Skip to first unread message

Atanas Korchev

unread,
Nov 29, 2011, 5:00:46 AM11/29/11
to KnockoutJS
I am developing a jQuery plugin which extends <select> elements. I
want to provide binding to KO observable arrays which means that I
want to be notified when something changes (e.g. an item is removed
from the array). For the time being I couldn't find any way to get the
observable array to which the underlying <select> is bound to so I can
subscribe() for notifications. Can you think of some other way to
implement what I am after? Listening to DOM mutation events of the
<select> is not really an option because it does not work very well in
different browsers.

Here is the relevant code:

<select id="ticket" style="vertical-align: middle" data-bind="options:
tickets,
optionsCaption: 'Choose...',
optionsText: 'name',
optionsValue: 'price',
value: chosenTicket">
</select>

var viewModel = {
tickets: ko.observableArray([
{ name: "Economy", price: 199.95 },
{ name: "Business", price: 449.22 },
{ name: "First Class", price: 1199.99 }
]),
chosenTicket: ko.observable(),
resetTicket: function() { this.chosenTicket(null) }
};

ko.applyBindings(viewModel);

$("#ticket").kendoDropDownList(); // here is my

Koviko

unread,
Nov 29, 2011, 9:09:58 AM11/29/11
to knock...@googlegroups.com
You can use ko.dataFor to get the object associated with a DOM element.

var data = ko.dataFor($("#ticket")[0]);

rpn

unread,
Nov 29, 2011, 9:28:48 AM11/29/11
to knock...@googlegroups.com
Remember that ko.dataFor will give you the data that would be available for binding against an element.  It won't actually give you the exact data that was bound.  What I mean is that if you do ko.dataFor on the the ticket element, it will return your viewModel.  You would still need to know to look for the "tickets" observableArray.  That will likely be perfectly valid in many cases.

However, if you really need to get back to a specific piece of data, then another option might be to use a "meta-data" binding.  It could look like:

ko.bindingHandlers.meta {
    initfunction (elementvalueAccessor)
    {
        var data ko.utils.unwrapObservable(valueAccessor());
        for (var prop in data{
            if (data.hasOwnProperty(prop){
               ko.utils.domData.set(elementpropdata[prop])
            }  
        }
    
};



Then, you would use it like:
<select id="ticket" style="vertical-align: middle" data-bind="options:
tickets,
                optionsCaption: 'Choose...',
                optionsText: 'name',
                optionsValue: 'price',
                value: chosenTicket,
                meta: { ko_options: tickets }">
</select>

KO provides ko.utils.domData.set/get functions that allow you to associate data with a DOM element.  You could certainly use jQuery's $.data as well for the same purpose.

Here is a sample:

Atanas Korchev

unread,
Nov 29, 2011, 12:01:21 PM11/29/11
to KnockoutJS
Thank you for the reply! Is there any way to avoid extending
bindingHandlers? I would really avoid extending KO in any way.

Here is a demo showing what I am after http://jsfiddle.net/korchev/UVaKQ/35/

If you remove an item from the tickets both selects update as they
should (the corresponding <option> is removed). However my plugin is
not aware of this and shows the older (removed) option. I would love
to find a way to get notified about that change somehow.

rpn

unread,
Nov 29, 2011, 2:40:46 PM11/29/11
to knock...@googlegroups.com
OK- I understand what you are after.  I think that it will be challenging to handle this completely in your plugin (not requiring the KO dev to do anything else).  You can't directly get the list that the select is bound against from the element itself.  You can get the overall data that it is bound against, but not the actual array itself.

If you are able to get the observableArray, then you can create a manual subscription against it and run code whenever the array changes.


I can think of a few possible directions:
1- when a dev instantiates the kendoDropDownList allow them reference the list that they are bound against in an option.  The basic idea would be something like: http://jsfiddle.net/rniemeyer/NtEBa/.   The .refresh() function on the plug-in does not seem to actually refresh it in this case, but I am sure that you know what you would want to do to rebuild the display.

2- I think that ultimately Knockout devs would want a layer of bindings to use with Kendo, so that they can declaratively use the Kendo functionality.  A basic binding might look something like: http://jsfiddle.net/rniemeyer/qYFPQ/  Ultimately, you might want to be able to simply use a kendoDropDownList binding instead of the other KO bindings (options, optionsText, etc.).  Again it calls the .refresh() method that does not seem to refresh it currently via a manual subscription.  

3- I probably would not go this route, but you could use ko.dataFor to get the data object and then grab the "data-bind" attribute and locate the value passed to "options:" and use it as a key into the data object and create your manual subscription.  http://jsfiddle.net/rniemeyer/eAqDM/ (same thing about refresh() method)

Hope this helps.




  
Reply all
Reply to author
Forward
0 new messages