1

Consider the following HTML:

<select data-bind="options: assemblies, optionsText: 'Name', value: selectedAssembly">
</select>

Name <input type="text" data-bind="value: selectedAssembly().Name" />

I'm retrieving assemblies array via jQuery AJAX:

[{"Id":1,"Name":"Foo"},
{"Id":2,"Name":"Bar"}]

selectedAssembly is an observable and ().Name throws an exception. I need to change Name property reflecting changes at runtime in the select options. I've tried:

<p data-bind="with: selectedAssembly">
    Name <input type="text" data-bind="value: $data.Name" />
 </p>

This way i succeed in retrieving Name property, but its value changes are updated only when user select another option from the box and not in real time.

user1824269
  • 633
  • 1
  • 9
  • 18
  • Can you maybe create a sample jsfiddle? It's not clear from your question what is your exact problem... because your code should work just fine see here: http://jsfiddle.net/X8AaV/ – nemesv Dec 16 '12 at 16:41
  • Well, you definitely understood what i mean, but I'm receiving assemblies data from server, how can I make Name property observable? I'm getting **Message: TypeError: Cannot read property 'Name' of undefined;**, perhaps array is not correctly initialized? Here comes fiddle: http://jsfiddle.net/hx4b3/ – user1824269 Dec 16 '12 at 18:40

2 Answers2

1

Knockout cannot direct do what you want. It may be able to if you have the ID and Name fields containing the same value. But that isn't what you are asking for and I wouldn't normally do it myself.

You can do it by introducing a ko.computed. Below is what I have in a working fiddle at http://jsfiddle.net/photo_tom/P8R8b/7/.

vm.selectedName = ko.computed({
    read: function() {
        var localVar = this.selectedAssembly(); // need for v8 compiler.
        if (localVar) {
            return localVar.Name();
        }
    },
    write: function(value) {
        var testValue = value.toLowerCase();
        var selArray = vm.assemblies();
        var l = selArray.length;
        for (var i = 0; i < l; i++) {
            var s = selArray[i].Name();
            if (s.toLowerCase() === testValue) {
                vm.selectedAssembly(selArray[i]);
            }
        }

    },
    owner: vm
});.  

The way this works is the read property returns the Name property for displaying. The write property is the key. It searches the options array for a name match, then sets that array element as your current option.

However, in my applications, I would use an auto-complete combo box which allows the user to type there desired value directly into the select statement. Take a look at https://stackoverflow.com/a/7538860/136717. That is how I'm doing my apps currently.

Community
  • 1
  • 1
photo_tom
  • 7,292
  • 14
  • 68
  • 116
1

You need to make the Name property observable on the items inside in the assemblies array to able to edit the items in the select.

You can do the JSON to object with observable properties conversion by hand or you can use the Knockout Mapping plugin which is written to solve this exact problem:

The usage is very simple:

function viewModel() {
    var self = this;    
    self.selectedAssembly = ko.observable();

    // you need to have a default item in assemblies
    // otherwise ko will set undefinied to selectedAssembly because it the 
    // assemblies collection is empty at the begining
    // which brakes the selectedAssembly().Name binding
    self.assemblies = ko.observableArray([{Name: ""}]);    

    $.getJSON('http://localhost:9000/api/assemblies', function(data){
        ko.mapping.fromJSON(data, {}, self.assemblies)
    });
}

A simplified JSFiddle with hardcoded data.

nemesv
  • 138,284
  • 16
  • 416
  • 359