Question

I'm working on an ASP.Net MVC application. My action is returning a view with a model that is an array of objects (a class with properties like Name, ID, IsViewable).

var model = @Model.ToJson(); // done via extension call

I want to observe this array, so whenever it changes I can update a table that has been bound to a template.

var viewModel = {
    accounts = ko.observableArray(model)
}

This works just fine for adding and deleting elements from the array. However, I also want the template to update when a property in one of the accounts changes (ie, Name or ID).

On the KnockoutJS website, it says: Of course, you can make those properties observable if you wish, but that’s an independent choice. This is what I cannot figure out how to do.

I tried something like this with no avail:

var viewModel = {
    accounts = ko.oservableArray([])
}

for(var i = 0; i < model.length; i++) {
    ko.observableArray(model[i]);
    viewModel.accounts.push(model[i]);
}

I can post the template and the table if it's needed.

Was it helpful?

Solution

You should look into the knockout.mapping plugin. I think it does everything you are looking to do.

OTHER TIPS

I ended up getting this to work, so I thought I would share with anyone that might have having the same problem.

You need to wrap your array items in a JavaScript class. Then in the constructor, set each property to obserable:

var model = @Model.ToJson();

var viewModel = {
    accounts = ko.observableArray(ko.utils.arrayMap(model, function(account) {
        return new AccountWrapper(account);
    }))
};

function AccountWrapper(account) {
    this.Property1 = ko.observable(account.Propery1);
    this.Property2 = ko.observable(account.Propery2);
    this.Property3 = ko.observable(account.Propery3);
}

ko.applyBindings(viewModel);

And if you want to modify one of the items directly to see the change, you could do something like:

viewModel.accounts()[3].Name('My Name Changed');

And you can still get notified when items are added or remove:

viewModel.accounts.remove(viewModel.accounts()[4]);

Here's another approach that works and doesn't require the mapping plugin:

var model = @Model.ToJson();

var viewModel = {
    accounts: ko.observableArray([]),

    fromJS: function(js) {
        for (var i = 0; i < js.length; i++) {
            this.accounts.push({ 
                Property1: ko.observable(js[i].Property1),
                Property2: ko.observable(js[i].Property2),
                Property3: ko.observable(js[i].Property3)
            });
        }
    }
};

viewModel.fromJS(model);
ko.applyBindings(viewModel);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top