I am trying to convert this answer to use ko.validation.
I am stuck at getting the next button to enable only when the model state is valid, however my code below is not evaluating correctly:
self.modelIsValid = function() {
self.currentStep().model().isValid();
};
I am currently stuck and would appreciate some fresh eyes to take a look. The model does validate OK (with error messages shown) the final step will be to validate each model with the modelIsValid which controls the next button.
A jsfiddle is here, and code below.
ko.validation.configure({
insertMessages: false,
decorateElement: true,
errorElementClass: 'error'
});
function Step(id, name, template, model) {
var self = this;
self.id = id;
self.name = ko.observable(name);
self.template = template;
self.model = ko.observable(model);
self.getTemplate = function() {
return self.template;
};
}
function ViewModel(model) {
var self = this;
self.nameModel = ko.observable(new NameModel(model));
self.addressModel = ko.observable(new AddressModel(model));
self.stepModels = ko.observableArray([
new Step(1, "Step1", "nameTmpl", self.nameModel()),
new Step(2, "Step2", "addressTmpl", self.addressModel()),
new Step(3, "Confirmation", "confirmTmpl", {NameModel: self.nameModel(), AddressModel:self.addressModel()})]);
self.currentStep = ko.observable(self.stepModels()[0]);
self.currentIndex = ko.computed(function() {
return self.stepModels.indexOf(self.currentStep());
});
self.getTemplate = function(data) {
return self.currentStep().template();
};
self.canGoNext = ko.computed(function() {
return (self.currentIndex() < (self.stepModels().length - 1));
});
self.modelIsValid = function() {
self.currentStep().model().isValid();
};
self.goNext = function() {
if (((self.currentIndex() < self.stepModels().length - 1) && ($('.validationMessage:visible').length <= 0))) {
self.currentStep(self.stepModels()[self.currentIndex() + 1]);
}
};
self.canGoPrevious = ko.computed(function() {
return self.currentIndex() > 0;
});
self.goPrevious = function() {
if ((self.currentIndex() > 0 && ($('.validationMessage:visible').length <= 0))) {
self.currentStep(self.stepModels()[self.currentIndex() - 1]);
}
};
}
NameModel = function (model) {
var self = this;
//Observables
self.FirstName = ko.observable(model.FirstName).extend({ required: true });
self.LastName = ko.observable(model.LastName).extend({ required: true });
return self;
};
AddressModel = function(model) {
var self = this;
//Observables
self.Address = ko.observable(model.Address).extend({ required: true });;
self.PostalCode = ko.observable(model.PostalCode).extend({ required: true });;
self.City = ko.observable(model.City).extend({ required: true });;
return self;
};
var viewModelFromServer = {
"FirstName": "John",
"LastName": "Doe",
"Address": "123 Main St",
"PostalCode": "53201",
"City": "Milwaukee"
};
ko.applyBindings(new ViewModel(viewModelFromServer));
Edit to Kevin's answer
The final view was not showing, this turned out to be because no validation group was defined for the view, so changed you modelIsValid function to the following:
self.modelIsValid = ko.computed(function () {
if (typeof(self.currentStep().model().isValid) != "undefined") {
return self.currentStep().model().isValid();
}
else
return true; // no validation used for viewmodel, so just return true
});
This seems to work, although I am very much a javascript newbie!
you've applied the validation rules to the individual observables in your Name and Address model, but you are trying to use the grouped functionality without explicitly doing the grouping in each model:
NameModel = function (model) {
var self = this;
//Observables
self.FirstName = ko.observable(model.FirstName).extend({ required: true });
self.LastName = ko.observable(model.LastName).extend({ required: true });
ko.validation.group(self);
return self;
};
also, you will need to make modelIsValid
a computed observable:
self.modelIsValid = ko.computed(function() {
return self.currentStep().model().isValid();
});
updated fiddle - http://jsfiddle.net/jnTjW/4/