1
votes

I am trying to bind a select element on a page using knockoutjs. The select element needs to display option built from an observable array of objects. I have created a computed property to create an array of nicely formatted values using my observable array:-

//my observable array of objects
self.TestTypes = ko.observableArray([ { Test: 'A'}, {Test: 'B'}]);

//my computed function to create {value, text} objects formatted nicely
self.Options = ko.computed(function() {
    var options = [];
    for(var i = 0; i < self.TestTypes().length; i++) {
        options.push({ value: self.TestTypes()[i].Test, text: 'Test ' + self.TestTypes()   [i].Test });
    }
    return options;
}, self);

I can use this to effectively populate my select element options without an issue.

I also have an object (same type as the objects in my observable array), that should drive the value of the select element:-

self.Test = ko.observable(new TestModel(model.Test));

The definition of my example object is here (it's very simplified)

var TestModel = function(model) {
    var self = this;
    self.Test = ko.observable(model.Test);
};

Finally, I try to bind everything to my select element, but the value doesn't take notice of my self.Test object:-

<select data-bind="value: Test.Test, options: Options, optionsText: 'text', optionsValue: 'value'"></select>

I have a feeling that this has something to do with references, but I can't get my head around it. I have a full example of my (extremely simplified) code on jsFiddle. Does anyone know what I'm doing wrong?

1
I know you simplified your code to post here, but TestModel misses a return this. Is it the case on your real code? - GôTô
Why would I need to return this in the TestModel? This example on the knockout page doesn't have one. knockoutjs.com/examples/cartEditor.html - user1573618
Right, because you're invoking the function as a constructor via the new keyword, JS will return this inherently. As an aside, you might consider adding the following to ensure that the function is always executed as a constructor: if(!(this instanceof TestModel)) return new TestModel(model); - Vinney Kelly

1 Answers

0
votes

Found a solution, I've expanded my original example a bit. My main aim was for the dropdown to drive a change of the Test object to a new one in the list of TestTypes. I stumbled upon the knockoutjs mapping plugin which I was able to use to get this working.

Essentially, my problem was that I couldn't declare my TestModel as a ko.observable because the nested properties don't become observables in the way I wanted. But I wanted it to be an observable so that I could easily update the entire TestModel by assigning it to one of the instances from my TestTypes array. In my first fiddle, you can fix it if you just remove the ko.observable from the self.Test in the main model, making it:-

self.Test = new TestModel(model);

Anyway, in case anyone is interested my updated jsFiddle is here.