0

Knockout.js docs says about observableArray

Simply putting an object into an observableArray doesn’t make all of that object’s properties themselves observable. Of course, you can make those properties observable if you wish, but that’s an independent choice.

But it doesn't specify how to make a property observable.

I have an array of users and I would like to make the property name observable to be able to change it from another viewmodel under some action.

This is what I've tried without success:

Reproduction online

var shouter = new ko.subscribable();

function usersViewModel() {
    var myData = [array of objects];
    var self = this;
    self.selectedRow = ko.observable();
    self.items = ko.observableArray(myData);

    self.selectedRow.subscribe(function (newValue) {
        console.log(newValue);
        shouter.notifySubscribers(newValue, "selectedRow");
    });

}

function departmentViewModel() {
    var self = this;
    self.row = ko.observable();

    //getting info from another view model
    shouter.subscribe(function (user) {
        self.row(user);
        console.log(self.row());

        self.row().name = ko.observable('Testing!!!');
    }, this, "selectedRow");

}

var MasterViewModel = function () {
    this.users = new usersViewModel();
    this.department = new departmentViewModel();
}

var mm = new MasterViewModel();
ko.applyBindings(mm);

How could I do it?

Alvaro
  • 40,778
  • 30
  • 164
  • 336

1 Answers1

2

You should make a User class that has name as an observable property.

function User(userData) {
    userData = userData || {};
    this.id = userData.id;
    this.name = ko.observable(userData.name);
    this.status = userData.status;
}

function UsersViewModel() {    
    var myData = [{
        id: "001",
        name: "Jhon",
        status: "Single"
    }, {
        id: "002",
        name: "Mike",
        status: "Married"
    }, {
        id: "003",
        name: "Marrie",
        status: "Complicated"
    }];

    self.users = ko.observableArray(myData.map(function(userData) {
        return new User(userData);
    });
}

If for some reason you want to avoid creating a User class then you'll need to transform the name property into an observable some other way. Like so...

myData.forEach(function (data) {
    data.name = ko.observable(data.name);
});
self.users = ko.observableArray(myData);
CrimsonChris
  • 4,651
  • 2
  • 19
  • 30
  • No other way around? What if a user has 20 or 30 properties? It seems a bit unnecessary to create a class for it, which makes it more difficult to maintain if the number of those properties increase or decrease with time. – Alvaro Jul 16 '15 at 13:35
  • You're basically asking "why should I bother making domain models?" http://www.makinggoodsoftware.com/2010/05/17/how-to-create-a-good-domain-model-top-10-advices/ – CrimsonChris Jul 16 '15 at 13:47
  • I would consider it *very* bad practice to directly bind my views to data coming from a server. There should be a layer in between. – CrimsonChris Jul 16 '15 at 13:53
  • Well, still I don't see how I can change it from another model in [my example](http://jsfiddle.net/imac/qWmat/117/) in which I receive the info by a subscriber. – Alvaro Jul 16 '15 at 13:53
  • Just a quick question: isn't there a way to directly map into the `User` class the properties of the `myData` that won't be modified at all? For example: `name` or `id`. It seems that when dealing with a big object, having to specify them in the `User` is kind of repetitive. – Alvaro Aug 13 '15 at 11:40
  • I'm [using this](https://gist.github.com/alvarotrigo/151fabf2d5d0e789aafc), but maybe there's a better way? – Alvaro Aug 13 '15 at 11:46