The simpliest way to subscribe for an observableArray in knockoutjs

Kasheftin picture Kasheftin · May 10, 2013 · Viewed 7.5k times · Source

I have an observableArray with some markers, for example:

var markers = ko.observableArray([
  { 
    id: 0, 
    title: ko.observable("Marker 0"), 
    lat: ko.observable(55.31),
    lng: ko.observable(11)
  },
  {
    id: 1, 
    title: ko.observable("Marker 1"), 
    lat: ko.observable(57.20),
    lng: ko.observable(15.5)
  }
]);

This array is sent to some MapWidget object, that has to create google map markers for each element. It has to move markers in case lat,lng observables change, change marker's title in case title observable changes and so on.

That means in MapWidget there's some array of googlemap markers, and it should be connected with the given observableArray. What is the best and the simpliest way to connect them?

Upd. More details about MapWidget.

MapWidget is some object that has an access to some google maps map object, and it receives as an argument an observableArray with markers like that one above.

var MapWidget = function(markers) {
  var div = $("#map").get(0);
  this.map = new gmaps.Map(div);
  /* 
    The magic goes here:
    markers is an observableArray, we subscribe for it's changes, 
    create gmaps.marker for each new element, 
    destroy in case of destroying them from array, 
    move and rename each marker in case of corresponding changes
  */
}  

Answer

Jalayn picture Jalayn · May 10, 2013

You could subscribe to your array like this:

ar.subscribe(function() {
  // clean and redraw all markers
});

If you do that, you will receive a notification when an item is added/removed to/from the array. BUT not when an property of an item is modified.

If you want to update your google maps markers based on individual property changes in items, you could implement a simple dirty flag mechanism and then subscribe individually to each item. Since each item has an id, which I presume is unique, you could create a map with key/value pairs being the id and the map widget.

So, given each individual item:

var item = function(data) {
  var self = this;
  self.isChanged = ko.observable(self);
  self.id = data.id;
  self.title = ko.observable(data.title);
  self.title.subscribe(function() { self.isChanged(self); self.isChanged.valueHasMutated(); });
  self.lat = ko.observable(data.lat);
  self.lat.subscribe(function() { self.isChanged(self); self.isChanged.valueHasMutated(); });
  self.lng = ko.observable(data.lng);
  self.lng.subscribe(function() { self.isChanged(self); self.isChanged.valueHasMutated(); });
}

And given a hypothetic map where you keep a link between your markers and the items:

var markersMap = [];
markersMap[0] = yourGoogleMapWidget;

You can then subscribe to track changes on items like this:

ar[0].isChanged.subscribe(function(item) {
  var myGMapMarker = markersMap[item.id()];
  // update your marker, or destroy and recreate it...
});