Search code examples
knockout.jsknockout-mapping-plugin

Knockout: multiple view models subscribe to Json


I would like to be able to organize my Knockout code, like this:

  • Import a 'Master Json' file and map it.
  • Create multiple view models, which each reference particular sections of the 'Master Json' file.
  • Bind all the observables in every view model to the values of the 'Master Json' file.

Is it possible to use ko.subscribable to have everything subscribe to my json file as the master value?

Or how can I move the $.getJSON (and save) functions from inside of each view model function? Can I make a function so the getJSON results are re-usable in each of these view models?

function ApplesViewModel ( data ) {

    // ko.observables here 

    //Get Json
    $.getJSON("json/masterJson.json", function(allData) {
        var mappedSlides = $.map(allData, function(item) { return new Slide(item) });
        self.slides(mappedSlides);
    });    

    //Save Json
    self.save = function() {
        $.ajax("/masterJson", {
            data: ko.toJSON({ slides: self.slides }),
            type: "post", contentType: "application/json",
            success: function(result) { alert(result) }
        });
    }; 
}

function BananasViewModel( data ) {
    //same as ApplesViewModel but with different observables
}

function CarrotsViewModel( data ) {
    //same as ApplesViewModel but with different observables
}

ko.applyBindings(new ApplesViewModel());
ko.applyBindings(new BananasViewModel());
ko.applyBindings(new CarrotsViewModel());

I don't want to import it many times because I think that will make a new copy each time. It should only be one Json file. When I try to move it, then I get errors that everything is undefined.

Maybe this is completely wrong approach. Does everyone put everything all into one view model? Won't it get confusing quickly?


Solution

  • Yes you can use ko.subscribable for that and if you do then checkout ko.postbox, a utility wrapper for ko.subscriable. This will allow you to publish your json across view models.

    So using ko.postbox, you will publish and subscribe to events/topics. Make a separate function for loading your JSON from server, and make this publish an event and all your subscribers of that event will know whenever the data is received.

    function DataLoader(){
      var self = this;
      self.loadJSON = function(){
        //Load JSON using getJSON from server and publish event "JsonDataLoaded"
        setTimeout(function(){
              ko.postbox.publish("JsonDataLoaded", data);
        }, 3000)
      };
    }
    
    var loader = new DataLoader();
    loader.loadJSON();
    

    So whenever you need new data, you will call loader.loadJSON' and upon success, that will yield event/topic 'JsonDataLoaded. You can then subscribe to this event in your view models. Something like

    function ApplesViewModel(data) {
      var self = this;
    
      ko.postbox.subscribe("JsonDataLoaded", function(newData){
        ko.mapping.fromJS(newData,{},self);
      },self,true);
    
    }
    

    CodePen reference.