Laura Silvani
Laura Silvani

Reputation: 757

Knockout.js - Pub/Sub between components, two-way conversation

my Knockout app is made of different components representing the different part of a form - so I have a "languages" component, a "countries" component, a "tags" component and so on. In each of these components I have an observable array to which I subscribe and send notifications through ko.postbox.notifySubscribers anytime these observables change. In another component, called filters, which serves as a collector of all the filters selected by the user in the form, I receive the changes in these observables through ko.postbox.subscribe.

My question is: what if I want to make changes in the filters component and having them reflected in the other components too? That is, how can I transform this one-way conversation in a two-way conversation?

For example, in the languages component I have a dropdown menu and user selects languages. In the filters menu I have the list with the selected languages (checkboxes), and I would like the user to be able to deselect items from here while still updating the dropdown in the main component.

Posting code for languages component and filters component, Thanks

Languages.js

ko.components.register("languages",{ 
 viewModel: function(){
  var self            = this;
  self.languages      = ko.observableArray();
  self.selectedLangs  = ko.observableArray();

  xhr.languages()
   .done(function (langs) {
     self.languages(langs);
  }); 

  self.selectedLangs.subscribe(function(values) {
    ko.postbox.notifySubscribers(
     _.map(values,function(val){
      var obj = _.findWhere(self.languages(), {id: val});
      return obj.text;
    }), "selectedLangs");
  });
},
template: 
'<fieldset id="language" class="wfp-u-1">\
  <div class="wfp-grid">\
    <label for="dss-language" class="wfp-u-1 wfp-u-lg-1-4">\
      Language <span class="loader"><i class="icon-loader"></i></span>\
      <em>Multiselection available</em>\
    </label>\
    <select id="dss-language" name="language" multiple="" class="wfp-u-1 wfp-u-lg-3-4 ui dropdown search selection"\
      data-bind="\
        options: languages,\
        optionsText: \'text\',\
        optionsValue: \'id\',\
        optionsCaption: \'Select one or more languages\',\
        selectedOptions: selectedLangs\">\
    </select>\
  </div>\
</fieldset>'
});

Filters.js

ko.components.register("filters",{ 
 viewModel: function(){

var self = this;
self.selectedLangs      = ko.observableArray();

ko.postbox.subscribe(function(langs) {     
  self.selectedLangs(langs);
}, self, "selectedLangs");
},
template: 
'<ul class="wfpList wfpFieldList mCustomScrollbar">\
  <li>\
    <a data-target="language">Language</a><i class="ico-angle-down"></i>\
    <!-- ko foreach: selectedLangs -->\
      <span data-bind="text: $data"></span>\
    <!-- /ko -->\
  </li>\
</ul>'
});

Upvotes: 1

Views: 1256

Answers (1)

Michael Best
Michael Best

Reputation: 16688

Copying John Papa's answer (https://stackoverflow.com/a/10818849/1287183):

An option is to create an isolated datacontext that maintains the models of observables. the viewmodels all look to the datacontext for their data and refer to the same objects, so when one updates, they all do. The VM's dependency is on the datacontext but not on other VMs. I've been doing this lately and it has worked well.

Upvotes: 1

Related Questions