See ValidatedViewModel from Karl Shreda.
When used in conjunction with an excellent knockout verification plugin, you can create verification restriction groups and apply them as needed.
Each time you run your validation procedure, you must delete all restriction groups, and then apply the restriction groups that you want for this step. Alternatively, subscribe to the step observed to set up restriction groups.
(I suggest using the try / catch statement when applying / removing restriction groups, since it will be erroneous if the restriction group has already been applied / removed.)
A bit of a learning curve is attached to this point, but it really helped me create a basket / checkout page with an appropriate check at every step.
Update: Here is the updated jsfiddle using ValidatedViewModel . I made the visible step dependent on the current Step observable and removed the necessary tags. All validation is now processed in the model. As a bonus, CSS in jsfiddle also styles the validation message without additional markup.
ko.validation.init({ parseInputAttributes: false, decorateElement: true, insertMessages: true, messagesOnModified: true, grouping: { deep: true, observable: true } }); var myViewModel = ValidatedViewModel(function () { var self = this; //observable init self.firstName = ko.observable(); self.lastName = ko.observable(); self.businessName = ko.observable(); self.referred = ko.observable(); self.referralFirst = ko.observable(); self.referralLast = ko.observable(); //navigation init self.currentStep = ko.observable(1); self.stepForward = function () { if(self.currentStep()<4){ self.changeSection(self.currentStep() + 1); } } self.stepBack = function () { if (self.currentStep() > 1) { self.changeSection(self.currentStep() - 1); } } self.changeSection = function(destIdx){ //remove all constraint groups try { self.removeConstraintGroup('step1'); } catch (e) { } try { self.removeConstraintGroup('step2'); } catch (e) { } try { self.removeConstraintGroup('step3'); } catch (e) { } //apply constraint group for current step try{self.applyConstraintGroup('step' + self.currentStep());} catch(e){} var errorCount = self.errors().length; self.errors.showAllMessages(); if(errorCount===0){ self.currentStep(destIdx); return true; } return false; } self.constraintGroups = { step1: { firstName: { required: true }, lastName: { required: true } }, step2: { businessName: { required: true } }, step3: { referralFirst: { required: true }, referralLast: { required: true } } } self.resetAll = function(){ //TODO return false; } this.errors = ko.validation.group(this); }); ko.applyBindings(new myViewModel());
HTML now looks like this:
<form id="register"> <h1>Current Step: <span data-bind="text:currentStep()"></span></h1> <fieldset data-bind="visible: currentStep()===1"> <h2>About You</h2> <ul> <li> <label for="firstName">First Name:</label> <input type="text" data-bind="value: firstName" /> </li> <li> <label for="lastName">Last Name</label> <input type="text" data-bind="value: lastName" /> </li> </ul> </fieldset> <fieldset data-bind="visible:currentStep()===2"> <h2>Your Business</h2> <ul> <li> <label for="businessName">Business Name:</label> <input type="text" data-bind="value: businessName" /> </li> <li> <label for="currentCustomer">Were you referred by someone?</label> <input type="checkbox" data-bind="checked: referred" /> </li> </ul> </fieldset> <fieldset data-bind="visible:currentStep()===3"> <h2>User Info</h2> <ul> <li> <label for="userName">Referrer First Name:</label> <input type="text" data-bind="value: referralFirst" /> </li> <li> <label for="password">Referrer Last Name:</label> <input type="password" data-bind="value: referralLast" /> </li> </ul> </fieldset> </form> <div class="nav-buttons"> <a href="#" data-bind='click: stepForward'>Continue</a> <a href="#" data-bind='click: stepBack'>Back</a> <a href="#" data-bind='click: resetAll'>Cancel</a> </div>