1
votes

I have a model being used by multiple view models, and i need some other javascript components to update the model, observed by my vm's. I have no idea how to do this since in the tutorial, they "mix" the model in the viewmodel.

Here is my code :

var ConversationModel = {
    conversations: ko.observableArray(),
    open: function(userId){
        for(var i = 0; i < this.conversations.length; i++){
            if(this.conversations[i].userId == userId){
                return;
            }
        }

        var self = this;
        var obj = ko.observable({
            userId: userId
        });

        self.conversations.push(obj);

        UserManager.getUserData(userId, function(user){
            $.getJSON(Routes.messenger.getConversation, "receiver=" + userId, function(data){
                obj.receiver = user;
                obj.data = data;
            });
        });
    }
};

function ConversationDialogViewModel() {
    var self = this;

    this.conversations = ko.computed(function(){
        return ConversationModel.conversations;
    });

    console.log(this.conversations());

    this.conversations.subscribe(function(context){
        console.log(context);
    });
}
1
You could try a pub/sub pattern using ko.postbox : github.com/rniemeyer/knockout-postbox - cl3m
Your multiple viewmodels make me think components would be a good idea. knockoutjs.com/documentation/component-overview.html - Roy J

1 Answers

0
votes

You can find a (reasonably) good example here how to combine:

  1. Components
  2. Per page ViewModel
  3. Central ServiceProviders (for example, to call APIs or to provide state information between different components)

Please note the code is ES2015 (new Javascript) but you can also write in plain Javascript if you want. The gulp script includes stringifying any html templates in the components, so they get combined and loaded as one file but are edited as separate elements.

An example component:

const ko = require('knockout')
    , CentralData = require('../../service-providers/central-data')
    , CentralState = require('../../service-providers/central-state')
    , template = require('./template.html');

const viewModel = function (data) {

    //Make service providers accessible to data-bind and templates
    this.CentralData = CentralData;
    this.CentralState = CentralState;

    this.componentName = 'Component One';
    this.foo = ko.observable(`${this.componentName} Foo`);
    this.bar = ko.observableArray(this.componentName.split(' '));
    this.barValue = ko.observable("");
    this.bar.push('bar');
    this.addToBar = (stuffForBar) => {
        if(this.barValue().length >= 1) {
            this.bar.push(this.barValue());
            CentralData.pushMoreData({firstName: this.componentName,secondName:this.barValue()});
        }
    };
    this.CentralState.signIn(this.componentName);
    if (CentralData.dataWeRetrieved().length < 10) {
        var dataToPush = {firstName : this.componentName, secondName : 'Foo-Bar'};
        CentralData.pushMoreData(dataToPush);
    }
};
console.info('Component One Running');
module.exports = {
    name: 'component-one',
    viewModel: viewModel,
    template: template
};

and component template:

<div>
    <h1 data-bind="text: componentName"></h1>
    <p>Foo is currently: <span data-bind="text: foo"></span></p>
    <p>Bar is an array. It's values currently are:</p>
    <ul data-bind="foreach: bar">
        <li data-bind="text: $data"></li>
    </ul>
    <form data-bind="submit: addToBar">
        <input type="text"
               name="bar"
               placeholder="Be witty!"
               data-bind="attr: {id : componentName}, value : barValue" />
        <button type="submit">Add A Bar</button>
    </form>
    <h2>Central State</h2>
    <p>The following components are currently signed in to Central State Service Provider</p>
    <ul data-bind="foreach: CentralState.signedInComponents()">
        <li data-bind="text: $data"></li>
    </ul>
    <h2>Central Data</h2>
    <p>The following information is available from Central Data Service Provider</p>
    <table class="table table-bordered table-responsive table-hover">
        <tr>
            <th>Component Name</th><th>Second Value</th>
        </tr>
        <!-- ko foreach: CentralData.dataWeRetrieved -->
        <tr>
            <td data-bind="text: firstName"></td><td data-bind="text: secondName"></td>
        </tr>
        <!-- /ko -->
    </table>
    <h3>End of Component One!</h3>
</div>

For your purposes, you can ignore the Central state provider and psuedo APIs, but you might find the model useful as your app gets more complicated.