Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to subscribe to observable in observableArray

I'm using KnockoutJS and trying to subscribe to an observable that is in an observableArray that is in an observableArray. So my viewModel looks like this...

function viewModel() {
    // private properties
    var self = this;

    // public properties
    self.movies = ko.mapping.fromJS([]);

    // subscriptions
    self.movies.UserMovies.Rating.subscribe(function(newValue) {
        console.log(newValue);
    });
}

The movies observableArray would look like this once populated from the mapping plugin...

[{
    Id: 1,
    Title: 'Movie1',
    Year: 2010,
    UserMovies: [{ Id: 11, Rating: 3.5, IsWatched: true }]
},{
    Id: 2,
    Title: 'Movie2',
    Year: 2010,
    UserMovies: [{ Id: 4, Rating: 4, IsWatched: true }]
}]

I'm trying to set up a subscription to UserMovies.Rating but, from my above viewModel getting the error message

TypeError: self.movies.UserMovies is undefined

How would I got about setting up a subscription to UserMovies.Rating when it is populated from the mapping plugin?

like image 455
bflemi3 Avatar asked Dec 20 '12 21:12

bflemi3


People also ask

How do you access an observable array?

Reading information from an observableArray So, you can get the underlying JavaScript array by invoking the observableArray as a function with no parameters, just like any other observable. Then you can read information from that underlying array. For example, alert( 'The length of the array is ' + myObservableArray().

Is array observable?

Observable<any[]) is Observable which contains an array. In this case, the array can contain any type, since it is typed by any. An array is only an object, which can hold more than one value at a time. The Observable object represents a push based collection.

How do you clear a knockout observable array?

To clear an observableArray you can set the value equal to an empty array.


1 Answers

Knockout does not provide the granularity for knowing which items changed in an array, just that something changed. You will need to loop trough the array each time an item is added or removed.

The foreach binding (via ko.utils.compareArrays) actually calculates the minimum number of operations to transform one array into another one, so that DOM-elements does not need to be recreated.

Using ko.utils.compareArrays, I was able to create a method that subscribes to array changes at an item level. Leveraging this, I could write a select method that manages the subscriptions.

http://jsfiddle.net/MizardX/s9M4z/

With the new select method, you could do this pretty concisely:

// Subscribe to items added to the array. The returned 'subscription' will be
// disposed of, when the item is later removed.
viewModel.movies.select(function (movie) {

    // Return the subscription. Same as above.
    return movie.UserMovies.select(function (userMovie) {

        // Subscribe to a non-array. The callback will receive the updated value,
        // instead of the an added item.
        return userMovie.Rating.select(function (rating) {

            // Executed each time a rating is updated.
            console.log(movie.Id(), userMovie.Id(), rating);
        });
    });
});

It handles additions, updates, and deletions as expected.

like image 182
Markus Jarderot Avatar answered Sep 19 '22 22:09

Markus Jarderot