Martin Brabec
Martin Brabec

Reputation: 3840

Knockout validation stops counting errors when data loaded

I'm working on form, where I'm using knockout + knockout.validation framework. Now I'm stuck with the validation, because it is not working as expected. I created simple fiddle to show you my problem.

My viewModel is complex object with observable fields and .extend methods with validation. The validation itself works well, but counting of errors does not.

In sort, if I assign data to my viewModel on start like this:

self.someObject = ko.observable(new SomeObject({
        id: "123",
        name: {
            en: "Initial data"
        }
    }));

everything is ok, but the call for accual data is asnyc call to WebApi. Therefore, the original object (viewModel.someObject) gets replaced when returned from server. The problem is, that once the object is replaced, viewModel.errors (which is validation group) stops keep tracking of errors.

You can see the full working example in JsFiddle example. Maybe I overlooked something important in JS (I'm more of a C# guy).

This is a big problem, because now I'm unable to stop saving of the data since I don't know if there is any error.

Thanks!

Upvotes: 0

Views: 212

Answers (2)

Martin Brabec
Martin Brabec

Reputation: 3840

So I finally found a solution. It seems so easy right now..

@Dandy made a point while saying, that the object gets replaced when the obejct is returned from ajax call. It is because the ajax call is async.

So, if you are in the same situation as me and you just want to make some initial load of the obeject (read the data only once per page load), here is a solution: You just need to make sure you have the data in javascript object BEFORE you call ko.applyBindings, ko.validation grouping and the object constructor, containing ko.observables. To do this, you can use jQuery.when(..).done(..). As I found out, code in "done" method will be called after all async calls in "when" method are finished.

I think you are getting the point here now. In "when" method get the object and save it to js variable. In "done" method, construct new object from it and call ko.observable etc., ko.applyBindings and validation grouping. I created simple example of the code, so check it out.

var user;
var viewModel;

// The User model with constructor
function User(data) {
    var self = this;

    self.username = ko.observable(data.username);
    self.address = ko.observable(data.address);
    // .. other properties
}

// UserViewModel object with constructor
function UserViewModel() {
    var self = this;

    // Make the User object from 
    self.User = ko.observable(new User(user));

    // have some other help properties here in view model
    // self.tasks = ko.computed(..); // etc..
}

// The magic
$.when(
    $.getJSON("yourUrl",
        null,
        function(data) {
            user = new User(data);
        })
).done(function () {
    // This code block will run after the async getJSON finishes

    viewModel = new UserViewModel();
    viewModel.errors = ko.validation.group(viewModel, { deep: true });

    // Activates knockout.js
    ko.applyBindings(viewModel);
});

Upvotes: 0

Dandy
Dandy

Reputation: 2177

The problem why the errors count is not working as expected is in your self.callForData function.

by doing

self.someObject(new SomeObject({
        id: "123",
        name: {
            en: "LoadedName"
        }
    }));

you are actually replacing someObject observable with a 'new' object instance, thus the old object validation becomes unreferenced.

You need a way to reset your current 'someObject' with new data values. I have updated the fiddle to show you one way how it can be done. Basically you introduce a 'reset' sort of function in your object, that resets the current state of object without creating new instance. Example: https://jsfiddle.net/newuserjs/ww2r6wet/

Upvotes: 2

Related Questions