If you have several view models on one page, how do you ensure that you can keep them synced? For example, if one item is added or a button clicked on one view model and you
The way I found to synchronising models is using the postbox library of RP Niemeyer
However I found something interesting regarding the observableArray. And that's why I created a new answer. Just to complete the answer from Niemeyer.
When using postbox and an observableArray the "subscribeTo" and "publishOn" events are fired when you add or remove an element from the observableArray. It doesn't fire anything when you update an element within the array. I think this doesn't have anything to do with the postbox library but a knockout limitation.
If you are attempting to get the event when updating an element of an observable array it's better to use "publish" and "subscribe" methods from the postbox library.
Please see the following FIDDLE
Code reference:
function FundEntity (fund)
{
var self = this;
self.id = fund.id;
self.fundName = fund.fundName;
self.description = fund.description;
self.isFavorite = ko.observable(fund.isFavorite);
}
function GridViewModel(model) {
var self = this;
self.fundList = ko.observableArray();
model.funds.forEach(function(fund) {
self.fundList.push(new FundEntity(fund));
});
self.favorite = function (id, index) {
var newValue = {
id: id,
index: index,
isFavorite: self.fundList()[index].isFavorite()
};
ko.postbox.publish("itemChanged", newValue);
return true;
};
self.isEditable = ko.observable().subscribeTo("myEditableTopic");
}
function FundDetailViewModel(model) {
var self = this;
self.fundList = ko.observableArray();
model.funds.forEach(function(fund) {
self.fundList.push(new FundEntity(fund));
});
ko.postbox.subscribe("itemChanged", function (newValue) {
self.fundList()[newValue.index].isFavorite(newValue.isFavorite);
});
self.editable = ko.observable(false).publishOn("myEditableTopic");
}
Knockout 2.0 does include functionality that lets you do basic pub/sub. Here is a sample where two view models communicate through a mediator.
var postbox = new ko.subscribable();
var ViewModelOne = function() {
this.items = ko.observableArray(["one", "two", "three"]);
this.selectedItem = ko.observable();
this.selectedItem.subscribe(function(newValue) {
postbox.notifySubscribers(newValue, "selected");
});
};
var ViewModelTwo = function() {
this.content = ko.observable();
postbox.subscribe(function(newValue) {
this.content(newValue + " content");
}, this, "selected");
};
ko.applyBindings(new ViewModelOne(), document.getElementById("choices"));
ko.applyBindings(new ViewModelTwo(), document.getElementById("content"));
The first view model notifies through the postbox on a specific topic and the second view model subscribes to that topic. They have no direct dependency on each other.
Certainly the postbox would not need to be global and could be passed into the view model constructor functions or just created inside a self-executing function.
Sample: http://jsfiddle.net/rniemeyer/z7KgM/
Also, the postbox
could just be a ko.observable
(which includes the ko.subscribable
functions).
I have created a small extension to solve this problem for a recent project of mine. Slightly similar in methodology, but adds subscriptions to the published observable directly, and will queue up subscribers if declared prior to the declaration of a published observable.
Knockout PubSub
You seem to be driving toward contradictory goals. The way you would do that in Knockout is to create observables, but yet you don't seem to want that.
If you have Foo and Bar objects with observables, you may not want observables on Foo that interact with bar or vice-verse, but why not have a Widget that watches Foo and Bar and mediates?