0

I ran into a case where selecting of an option that is data bound to my javascript data object resulted into a different option being shown as selected.

Example data object:

var data = {
    "person": { "no" : 2 },
    "options": [
        {"no":0,"t":"null"},
        {"no":1,"t":"a"},
        {"no":2,"t":"b"},
        {"no":3,"t":"c"}
    ]
};

Corresponding Select with the data binding setup:

<script id="SelectTemplate" type="text/x-jsrender">
    <select id="MySelect" data-link="person.no">
        <option value=""></option>
        {^{for options}}
        <option data-link="{:t} value{:no} selected{:no === ~root.person.no}"></option>
        {{/for}}
    </select>
</script>

I have created a JsFiddle here to play with my setup. Can anyone please explain to me why this is happening? Or what I am doing wrong?

Note: I would like to share my current workaround just in case someone else happens to run into this problem. I am listening to property change event on my select data bound object (i.e. person). Here is what it looks like:

$(data.person).on('propertyChange', function (event, eventArgs) {
    $("#MySelect").val(eventArgs.value);
});
Parth Shah
  • 2,060
  • 1
  • 23
  • 34

1 Answers1

1

The problem is that you are using integer values: no:1 rather than no:"1".

Those values get converted to string when set as values of the corresponding option element. So when the user selects a new item, the value person.no is set to a string "2", say, which fails the comparison :no === ~root.person.no.

So here are four alternative fixes:

1: If you don't want to support having dynamic changes to the t or no values, then you don't need to data-link those values on the option, but can set them statically in the initial rendering:

<select data-link="person.no">
    {^{for options}}
        <option value="{{:no}}">{{:t}}</option>
    {{/for}}
</select>

-but person.no will still get switched to a string value after selection, unless you use a converter: <select data-link="{:person.no:toNum}"> (see #4 below...)

2: If you want to keep the data-bound values, you can change from using from integers to using string values.

3: Keep the integers and simply make the comparison non-strict: <option data-link="{:t} value{:no} selected{:no == ~root.person.no}"></option> - but again, person.no will get switched to a string value after selection...

4: Keep the integers and convert back to integer, so the number type is preserved:

$.views.converters("toNum", function(val) {
    return +val;
})

and

<select data-link="{:person.no:toNum}">
    {^{for options}}
        <option data-link="{:t} value{:no} selected{:no === ~root.person.no}"></option>
    {{/for}}
</select>

(So now person.no will have a number value after selection...)

BTW - side note: for your workaround you could have used an alternative - specific for changes to person.no, so it will not break if other properties of person get changed:

$.observe(data.person, "no", function (event, eventArgs) {
    $("#MySelect").val(eventArgs.value);
});

rather than

$(data.person).on('propertyChange', function (event, eventArgs) {
    $("#MySelect").val(eventArgs.value);
});
BorisMoore
  • 8,444
  • 1
  • 16
  • 27