2
votes

I have spent the last few days researching the following KnockoutJS issue.

I have a page with 3 viewmodels on. I am using div's with id's to specify the bindings i.e:

ko.applyBindings(new viewModel(datasource), $("#sectionQualifications")[0]);

I am also using RequireJS which has really helped with making my app modular and works well with KnockoutJS of course.

My question relates to having (as mentioned) 3 viewmodels on my page.. none overlap, but each viewmodel has a SAVE function. So a quick look at one of the viewmodel snippets:

function viewModel(data) {
        self = this;

        self.quals = ko.observableArray(data),

        self.addQual = function () {
            self.quals.push(new qualification());
        },

        self.remove = function (item) {

            //  Remove from the database IF we have an actual record in our viewmodel
            if (item.Id !== 0) {
                dataservice_qualifications.deleteEntity(item.Id, ko.toJSON(item),
                    {
                        success: function (ret) {
                            common.notifyOK('Qualification removed');
                        },
                        error: function (err) {
                            common.notifyError('Cannot remove that qualification');
                            console.log('Qualification Remove Error', err);
                            console.log('Remove error object', this.Id);
                        }
                    }
                );
            }

            //  Remove from the actual view model
            self.quals.remove(item);
        }

        //  Save and move on.. we need to iterate through the qualifications, update any existing rows (ID NOT 0) or
        //  add new entries (ID IS 0)
        self.save = function () {

            var saveData = ko.toJS(this.quals);
            for (var i in saveData) {

                //  New qualification entry
                if (saveData[i].Id === 0) { // New qualification entry
                    dataservice_qualifications.postEntity(ko.toJSON(saveData[i]),
                        {
                            success: function (ret) {
                            },
                            error: function (error) {
                                common.notifyError('Cannot add qualification ' + saveData[i].qualificationName);
                                console.log('Qualification add error', error);
                            }
                        }
                    );
                } // eof NEW qualification

                if (saveData[i].Id > 0) {
                    dataservice_qualifications.putEntity(saveData[i].Id, ko.toJSON(saveData[i]),
                        {
                            success: function (ret) {
                            },
                            error: function (error) {
                                common.notifyError('Cannot update qualification ' + saveData[i].qualificationName);
                                console.log('UPDATED: ERROR:', error);
                            }
                        }
                    );
                } // eof UPDATED qualification

            } // eof saveData loop

            common.notifyOK('Qualifications updated');

        } // eof savenext function

        return;
    };

So from that above sample, I would have 2 other viewmodels that are similar, which have the SAVE function as above. So of course I want to use say jQuery to click a button and save all 3 viewmodels (i.e. via that SAVE function on each).

Because I am using RequireJS, I have tried exposing a "public" function that in turn tries to internally call the viewModel.save() function as follows in this snippet:

function saveModel() {
    viewModel.save();

}

//  PUBLIC INTERFACE
return {
    saveModel: saveModel,
    loadViewModel: koMapData
}

So the theory being I can call the "saveModel" function from wherever which should trigger the save of the viewmodels?

Any help really appreciated. By the way I have gone down the path of trying to create the viewmodel like:

var viewModel = {

    save: function() {
        blah blah...
    }
}

however no real luck in that either? I am sure I am missing something simple, because I would think you could/should be able to trigger a function from a viewmodel externally somehow?

EDIT Just for reference, the models DO NOT overlap..

Thanks in advance, David.

2
Did you find this answer useful?Ali Habibzadeh

2 Answers

2
votes

You can merge view models in an object like this:

var mainVModel = {
    vModel1: { // has save method},
    vModel2: { // has save method},
    saveAll : function(){
        mainVModel.vModel1.save();
        mainVModel.vModel2.save();
    }
}

ko.applyBindings(new mainVModel());
0
votes

Actually thanks @XGreen for your suggestion, and I can see that working well, however I am using requirejs, and so my app structure isn't quite a match.

However, I have been successful in the following solution:

First, I created the viewmodel in a slightly different way..

var viewModel = {
  quals: ko.observableArray([]),

  addQual: function () {
    viewModel.quals.push(new qualification());
  },

  remove: function (item) {
    // Do the remove bit..
  },

  save: function () {
    var saveData = ko.toJS(viewModel.quals);
    // Do the save stuff..
  } // eof savenext function

}; // eof viewModel def

So the viewmodel is defined, and then I had a helper function to access just the SAVE function of the viewmodel:

//  Private:    Allows external access to save the viewmodel
var viewModelFunctions = {
    saveModel: function () {
        viewModel.save();
    }
}

.. and then finally, because I am using the revealing module pattern within the requirejs structure, I created a public interface function as follows:

function koMapData(incomingData) {
    datasource = (incomingData === null) ? [new qualification()] : incomingData;
    viewModel.quals = ko.observableArray(ko.toJS(datasource));
    ko.applyBindings(viewModel, $("#sectionQualifications")[0]);
}

//  PUBLIC INTERFACE
return {
    viewModelFunctions: viewModelFunctions,
    loadViewModel: koMapData
}

So you can see the last part with the viewModelFunctions, which means in another module (or wherever) I can reference/trigger the SAVE function remotely by:

mymodule.viewModelFunctions.saveModel()

Which also means (in my case because I have 3 viewmodels I need to trigger save for from one event) I can have the flexibility of saving when/how I want to. Obviously it would be good to return any errors too etc up to the calling module, but that is in principle how it works for me.