2012-12-20 14 views
5

Ich benutze KnockoutJS und versuche, eine observable, die in einer observableArray ist, die in einem observableArray ist. So sieht mein Ansichtsmodell so ...Wie abonniere ich Observable in ObservableArray

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); 
    }); 
} 

Die movies observableArray wie dieses eine Mal aus dem Mapping-Plugin bevölkert aussehen würde ...

[{ 
    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 }] 
}] 

Ich versuche, ein Abonnement für UserMovies einzurichten .Rating aber aus meinem oben Ansichtsmodell immer die Fehlermeldung

TypeError: self.movies.UserMovies is undefined

Wie würde ich mich über ein Abonnement fürEinrichtung bekamwenn es aus dem Mapping-Plugin besteht?

+0

Sie können die neueste Version dieses Plugins verwenden, wenn Sie eingehende Abonnements für Arrays wünschen: https://github.com/ZiadJ/knockoutjs-reactor – Ziad

Antwort

7

Knockout bietet nicht die Granularität, um zu wissen, welche Elemente in einem Array geändert wurden, nur dass etwas geändert wurde. Sie müssen das Array jedes Mal durchlaufen, wenn ein Element hinzugefügt oder entfernt wird.

Die foreach Bindung (über ko.utils.compareArrays) berechnet tatsächlich die minimale Anzahl von Operationen, um ein Array in ein anderes zu transformieren, so dass DOM-Elemente nicht neu erstellt werden müssen.

Mit ko.utils.compareArrays konnte ich eine Methode erstellen, die Array-Änderungen auf einer Elementebene abonniert. Wenn ich dies nutze, könnte ich eine select Methode schreiben, die die Subskriptionen verwaltet.

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

Mit der neuen select Methode, könnten Sie dies ziemlich knapp tun:

// 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); 
     }); 
    }); 
}); 

Es behandelt Ergänzungen, Aktualisierungen und Löschungen, wie erwartet.

+0

Dies ist eine gute Antwort. Danke Markus :) – bflemi3

+0

@ bflemi3 Ich war mit der vorherigen Version nicht zufrieden, da Löschungen nicht behandelt werden konnten. Dieser ist robuster. –

2

Ich denke, Sie eine Schleife durch Ihre Filme haben werden und abonnieren Sie jedes Rating Unterkunft ein: die donwside hier

$.each(self.movies(), function(i, movie) { 
    movie.Rating.subscribe(function(newRatingValue){ /* ... */ }) 
}); 

Natürlich ist, dass Sie müssen auch auf das Array abonnieren selbst, Für Situationen, in denen Sie dem Array neue Filme hinzufügen und dann Änderungen manuell unter ihren Bewertungswert abonnieren.

0

Sie können Ihre Objekte in dieser Art und Weise Art abonnieren:

var UserMovies = function (data) { 
     this.Id = ko.observable(); 
     this.Rating = ko.observable(); 
     this.IsWatched = ko.observable(); 
     this.update(data); 
    } 

    ko.utils.extend(UserMovies.prototype, { 
     update: function (data) { 
      this.Id(data.Id || ""); 
      this.Rating(data.Rating || ""); 
      this.Rating.subscribe(function (newValue) { 
       console.log(newValue); 
      }); 
      this.IsWatched(data.IsWatched || ""); 
     } 
    }); 

Ich bin mir nicht sicher, was Sie vielleicht in der Lage sein zu tun zu tun oder nicht innerhalb des Objekts, um die Dinge aus der abonnieren möchte, aber dies tut Arbeit. Ich bin mir auch nicht sicher, ob jedes Abonnement einzigartig sein wird oder ob eine Rating-Änderung alle UserMovies Rating-Abonnements auslöst. Ich habe das nicht getestet. Ich habe das nur in einzelnen Objekten und nicht in Arrays von Objekten verwendet.

Verwandte Themen