chandu
chandu

Reputation: 233

pass the value from one viewmodel to another viewmodel in knockoutjs

When I searched,I found,how to bind values from viewmodel to view but not viewmodel to viewmodel I have a requirement to pass one property value from one viewmodel to another viewmodel as I need the initial property to be updated in first viewmodel then I want to use it in another viewmodel.because it is helpful while testing.

Assume that the below view model is the 1st view model,

var xx = xx || {};
    xx.yyy = xx.yyy || {};
    xx.yyy.zzz = function(object ) {
    var model = {};
    model.isTested= ko.observable(false);

   //below is the anonymous call to get the value(true/false):
    datasource.someFeatureEnable.isTested().done(function (featureToggle) {
    model.isTested(featureToggle.enabled);                
    });
}

I want to pass isTested(true/false) property value in another viewmodel because to run my application properly and make my tests pass

Upvotes: 3

Views: 4126

Answers (3)

user3174746
user3174746

Reputation:

It would depend on whether the two viewmodels are in memory and available at the same time. If so, then scaryman's suggestion of using knockout-postbox is a good one. Or, you could use a client-side message bus such as postal.js (which is what I use).

If the viewmodels are not in memory and available at the same time, then introducing a third, static object (we'll call it "Z") would be necessary to handle something similar to an Event Aggregator pattern. Essentially, viewmodel-1 would directly reference Z to store the isTested value there. When viewmodel-2 instantiates, it would check with Z to get the isTested value. This approach is not really an aggregation of events since you're not publishing messages on a channel (although, if you were to take the approach of sending messages with payload to Z, then it would be--way more complicated than it needs to be in this case).

If you're using AMD--say, require--you would simply require Z in each of your viewmodels (or, actually, in every viewmodel dependent upon Z). You can read about require at http://www.requirejs.org.

Bare bones, we would have:

viewmodel-1

this.isTested = ko.observable(false);
var Z = require("Z");
Z.isTested(this.isTested());

viewmodel-2

this.isTested = false;  //Making the assumption that isTested doesn't have to be observable here--but it could be

var Z = require("Z");
this.isTested = Z.isTested();

Of course, you would elaborate on this so that Z could handle an array of viewmodels, perhaps with a dictionary object that could look up the viewmodel in question.

I would caution against scaryman's approach, though, of making viewmodels dependent on each other. If you have many viewmodels that have an isTested relationship, the dependency graph could get pretty hairy. I would concentrate isTested and perhaps other properties in one, third-party module: Z.

I would reiterate that Z should be static. That means it should return an object literal, not a constructor function.

As a final note, I'm not suggesting the Mediator pattern, by the way. I'm not suggesting that Z should store references to each of viewmodel-1 and viewmodel-2.

Upvotes: 0

scaryman
scaryman

Reputation: 1900

You could make your second viewmodel have a dependency on your first viewmodel.

//this is the definition of your first view model.
function MainViewModel(dataSource) {
  var self = this;
  this.DataSource = dataSource;
  this.isTested = ko.observable(false);

  //a callable function that will run isTested check on someFeatureEnable
  this.TestSomeFeature = function() {
      self.DataSource.someFeatureEnable.isTested().done(function (featureToggle) {
          self.isTested(featureToggle.enabled);                
      });
  };
  return this;
}
//this is the definition of your second viewmodel
function SubViewModel(mainViewModel) {
  var self = this;
  self._mainViewModel = mainViewModel;
  //for read only access
  self.MainIsTested = function() { return self._mainViewModel.isTested(); }
  //for read/write
  self.MainIsTestedReference = self._mainViewModel.isTested
  return self;
}



//this is the code that initializes the whole page.
var main = new MainViewModel();
var sub = new SubViewModel(main);
//now run the check
main.TestSomeFeature();

//these are examples, showing how to get at the isTested property in your various viewmodels. The comments are what the code should return
sub.MainIsTested(); //false
main.isTested(); //false
//set isTested to true from the sub
sub.MainIsTestedReference(true);
//now main isTested returns true, because the sub viewmodel and the main viewmodel have references to the same object.
main.isTested(); // true

If you want to get more advanced and use an events based approach I'd recommend looking into ko.postbox, check out these references.

http://www.knockmeout.net/2012/05/using-ko-native-pubsub.html

https://github.com/rniemeyer/knockout-postbox

Upvotes: 3

Anders
Anders

Reputation: 17564

I would use a event aggregator pattern, this is a decoupled and powerfull way for any listener to listen to any event without couppling to the publisher.

You can check how I did that for this library (SignalR library)

https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/

I extracted the client side only code for your convenience

http://jsfiddle.net/ezkGt/

basicly what you do to pub / sub is

signalR.eventAggregator.subscribe(Event, listener.onEvent, listener);

setInterval(function() {
    signalR.eventAggregator.publish(new Event(new Date()));
}, 500);

setTimeout(function() {
    signalR.eventAggregator.unsubscribe(listener);
}, 5000);

Upvotes: -1

Related Questions