I ran into a situation with my code the other day while using knockoutjs (it’s a great js library for creating mvvm web apps). Here’s my html (which is bound to a knockout model).
<input class="filter-input text-box single-line" type="text" data-bind="value: Value, if: ListItems().length == 0" />
<select data-bind="if: ListItems().length != 0, options: ListItems, optionsValue: 'Value', optionsText: 'Text', value: Value, optionsCaption: '-'"></select>
Seems simple enough. If I have an array of list items, that means I want a dropdown list. So, I show the input field if the listitem length is 0. Otherwise, I show the select. Note one important thing: they’re both bound to the property ‘Value’.
The problem I ran into is that when I’d change value in a textbox, the value would disappear when the control lost focus. It drove me crazy until I finally realized what was happening. When you change the value in the textbox, since it’s bound to an observable, the dropdown list gets notified of the change as well. The dropdown list then tries to bind to the new value, but it can’t, since it doesn’t have any items, and therefore can’t find a match. Since it can’t find a match, it’s value is empty, and that in turn is passed back to the observable, which in turn passes it back to the textbox, causing it to lose the value that was initially entered.
Whew! It makes sense now, but it wasn’t immediately obvious to me at the time. My initial solution was to have a ‘Value’ property and a ‘SelectValue’ property, and have each control bind to the corresponding property. This meant I had to change my server control.
Fortunately, it turns out it’s much simpler to fix. I had completely forgotten that knockoutjs has an if statement. I changed my code to wrap the input and select in an if, and voila! It worked. There are no issues since the controls aren’t even created thanks to the ifs.
<!-- ko if: ListItems().length == 0 -->
<input class="filter-input text-box single-line" type="text" data-bind="value: Value" />
<!-- /ko -->
<!-- ko if: ListItems().length != 0 -->
<select data-bind="options: ListItems, optionsValue: 'Value', optionsText: 'Text', value: Value, optionsCaption: '-'"></select>
<!-- /ko –>
That’s it. A big thanks to Ryan (creator of knockoutjs) for the if reminder on google groups (full thread here).