2
votes

I'm building a huge page that has 10 different sections on it and am using Knockout for event binding, etc.

Each sections contains a form with it's own viewModel with it's fields and validation properties, etc. I patterned it after reading this post regarding multi-view models.

I have a masterViewModel that imports lots of subViewModels. This is all working fine and I can set up observable elements that auto-populate upon field input, etc.

I am binding my form submit to a function in my viewModel like below.

After I validate and save the form fields (via ajax post), I want to put the section into a read-only mode, but I don't know how to get a handle on my viewModel in my ajax call's success callback.

<form action="webservice.php" method="POST" data-bind="submit: contactInformation.validateSubmit">


this.validateSubmit = function(formElement){

    var result = ko.validation.group(this, {deep: true});
    if (!this.isValid()) {
        result.showAllMessages(true);
        return false;
    }

    //actually save stuff, call ajax, submit form, etc;
    //  setup a promise
    var posting = $.post( "./webservice.php", $(formElement).serialize() );

    posting.done(function( data ) {
        this.contactInformation.model_state("summary"); // Uncaught TypeError: Cannot call method 'model_state' of undefined 
        // i also tried the line below, instead of line above...
        ko.mapping.updateFromJS(this, data); // Uncaught TypeError: Cannot call method 'updateFromJS' of undefined 
    });

};

Does anyone have any idea how to do this? I have a handle of the formElement from the contactInformation.validateSubmit() function. Do I need to manually subscribe to a listener somewhere? Or is there a way to hang the model off of $(formElement).data('model')?

Any help would be welcomed.

Thanks, -- Scott

3

3 Answers

2
votes

Maybe you can structure your call a little differently so that your form submit is tied to a function on the master view model

<form data-bind="submit: submitContactInformation">

Then, in your view model, make the call to your sub view model separately:

var ViewModel = function () {
    var self = this;

    self.contactInformation = ...import sub view model

    self.isContactInformationReadOnly = ko.observable(false);

    self.submitContactInformation= function () {
        //make ajax call
        if( contactInformation.validateSubmit() ){
            self.isContactInformationReadOnly(true);
        }
    };
};

var vm = new ViewModel();
ko.applyBindings(vm);
0
votes

Well Joseph's method may have worked but before I could test it, I removed the submit handler and, instead, used click handlers of the save/edit buttons because they contain a handle to the model. I then realized that since there would be a save & edit function for each model and that each one would do the same thing, across models, I moved it up into the masterViewModel. I then reference them by

data-bind: "click: $root.save"

Then, that save method contains most of the stuff in that submit function, plus some info to set model_state properties:

    this.save = function(model, event) {
        log(model);

        var $form = $(event.target).closest('form');

        var result = ko.validation.group(this, {deep: true});
        if (!this.isValid()) {
            result.showAllMessages(true);
            return false;
        }

        //actually save stuff, call ajax, submit form, etc;
        var posting = $.post( $form.attr("action"), $form.serialize() );

        posting.done(function( data ) {
            if (data.status === "success") {
                model.model_state("summary");
            } else {
                if (data.errors) {
                    // display errors
                }
            }
        });
    };
0
votes

I have done what you original asked like this:

<form id="registration-form" data-bind="submit: CreateUser.bind($root,$data)" method="POST">