0

So I have 2 view models, one that houses an array I use that binds each data node in dataList to the form, and the other is used as a model for each data node. I'm trying to update the array in the first viewmodel with the data from the second:

HTML:

<div data-bind="foreach:mainArray">
    <select data-bind="options: $root.ourTypes, optionsValue: 'ID', optionsText: 'Name', value: $data.OurTypeId"></select>
</div>

JavaScript:

var dataList = [        
    { OurTypeId: 4 }, // there are other values here... 
    { OurTypeId: 2 }, // and here...
    { OurTypeId: 3 }  // and here...
];
var ourTypes = [
    { ID:"1", Name:"None", Limit:0 },
    { ID:"2", Name:"Fruits", Limit:5 },
    { ID:"3", Name:"Vegetables", Limit:5 },
    { ID:"4", Name:"Meats", Limit:2 }
];
var myViewModel = new MyViewModel(dataList);
ko.applyBindings(myViewModel);

function MyViewModel(dataList) {
    var self = this;
    self.ourTypes = ourTypes;
    self.mainArray = ko.observableArray([]);

    if (dataList.length > 0) {
        for (var i=0; i<dataList.length; i++) {
            var myDataViewModel = new MyDataViewModel(dataList[i]);
            //alert(myDataViewModel.OurType);
            self.mainArray.push(myDataViewModel);
        } 
    }
}

function MyDataViewModel(vm) {
    var self = this;
    if (vm != null) {                     
        var myType = "";
        for (var i=0; i<ourTypes.length; i++) {
            if (ourTypes[i].ID == vm.OurTypeId)
                myType = ourTypes[i].Name;
        }
        self.OurTypeId = ko.observable(vm.OurTypeId);
        self.OurType = myType;
   } else {
       self.OurTypeId = 0;    
       self.OurType = "";    
   }
   self.OurTypeId.subscribe(function(newValue) {
       var updatedItem = 0;
       var newName = "";
       for (var i=0; i<ourTypes.length; i++) {
          if (ourTypes[i].ID == newValue) {
            self.OurType = ourTypes[i].Name;
            // alert(ourTypes[i].Name);
             updatedItem = i;
          }
       }
   //  I want to do something like this to update "OurType" in mainArray...
   //  var theList = MyViewModel.mainArray();
   //  theList[updatedItem].OurType = self.OurType;
   //  MyViewModel.mainArray(theList);
   });
}

This is actually a slimmed-down version of the code that just focuses on "ourTypes", my fiddle has a little more. I use the ID on the dropdown to indicate a change in the model, but then I have to update the actual name of the type manually (you'll see how the name does not update when the ID does in my fiddle), so I attempt to do that in a .subscribe() function, but nothing seems to work. Any ideas?

Fiddle: http://jsfiddle.net/navyjax2/ks6nbzp7/

1 Answer 1

1

Since the name of OurType is supposed to change automatically when you change the ID, this is a good job for a computed observable:

self.OurType = ko.computed(function() {

    return ko.utils.arrayFirst(ourTypes, function(type) {
        return type.ID == self.OurTypeId();
    }).Name;

});

When a computed observable runs, it takes note of all the other observables that are accessed — in our case here, that's just self.OurTypeId(). That's called a dependency. Whenever a dependency changes, the computed is automatically run again.

Here, we are using Knockout helper function ko.utils.arrayFirst to iterate over ourTypes and return the first element in it where the ID matches the self.OurTypeId() the user has selected. We then return the .Name of that ourType element.

JSFiddle

Please note that your if (vm != null) else clause is potentially trouble: the values you set there aren't observable.

self.OurTypeId = 0;

That means that MyDataViewModels where vm is null will be broken - Knockout will bind the <select> to a plain, non-observable "0" and will never update again.

Also, one time you are making self.SlotPosition an observable, another time you are making it an observableArray.

It is better to initialize the properties as observable once and only update their values:

function MyDataViewModel(vm) {
    var self = this;

    this.SlotPosition = ko.observable();
    this.OurTypeId    = ko.observable();

    if (vm) {
        this.SlotPosition(vm.SlotPosition);
        this.OurTypeId(vm.OurTypeId);
    }

    this.OurType = ko.computed(function() {

        if (!self.OurTypeId()) {
            return;
        }

        return ko.utils.arrayFirst(ourTypes, function(type) {
            return type.ID == self.OurTypeId();
        }).Name;

    });
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the response - looks great! To address your other concerns, yeah, I probably should change that Id = 0 to Id = 1. Was more of just demonstrating I set some default values if I don't get any data.
Happy to hear that! Just to make sure there's no misunderstanding - the problem isn't that you set id to 0. The problem is that you didn't assign it an observable. So id = 0 is problematic, while id = ko.observable(0) would have been fine. But the way it's structured above is better anyhow.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.