Looking for a sanity check here. I've recently started learning knockout, and have been instructed to convert an existing multi-step form.
The basic idea is to validate each step before allowing the user to continue. There are also certain restrictions set up (not shown) that determine whether to continue onward or submit using all of the current data (ex: if they don't qualify).
Here is a fiddle with a simplified version (the actual form contains about 40 fields over 4 steps)
http://jsfiddle.net/dyngomite/BZcNg/
HTML:
<form id="register">
<fieldset>
<h2>About You</h2>
<ul>
<li>
<label for="firstName">First Name:</label>
<input type="text" data-bind="value: firstName" required="required" />
</li>
<li>
<label for="lastName">Last Name</label>
<input type="text" data-bind="value: lastName" required="required" />
</li>
</ul>
</fieldset>
<fieldset>
<h2>Your Business</h2>
<ul>
<li>
<label for="businessName">Business Name:</label>
<input type="text" data-bind="value: businessName" required="required" />
</li>
<li>
<label for="currentCustomer">Were you referred by someone?</label>
<input type="checkbox" data-bind="checked: referred" />
</li>
</ul>
</fieldset>
<fieldset>
<h2>User Info</h2>
<ul>
<li>
<label for="userName">Referrer's First Name:</label>
<input type="text" data-bind="value: referralFirst" required="required" />
</li>
<li>
<label for="password">Referrer's Last Name:</label>
<input type="password" data-bind="value: referralLast" required="required" />
</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>
JS:
$("#register").children().hide().first().show();
ko.validation.init({
parseInputAttributes: true,
decorateElement: true,
writeInputAttributes: true,
errorElementClass: "error"
});
function myViewModel() {
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();
//validaiton observable init
self.step1 = ko.validatedObservable({
firstName: self.firstName,
lastName: self.lastName
});
self.step2 = ko.validatedObservable({
businessName: self.businessName,
referred: self.referred
});
self.step3 = ko.validatedObservable({
referralFirst: self.referralFirst,
referralLast: self.referralLast
});
//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){
var validationObservable = "step" + self.currentStep();
if(self[validationObservable]().isValid()){
self.currentStep(destIdx);
$("#register").children().hide().eq(self.currentStep() - 1).show();
return true;
}else{
self[validationObservable]().errors.showAllMessages();
}
return false;
}
self.resetAll = function(){
//TODO
return false;
}
}
ko.applyBindings(new myViewModel());
My questions:
Does it make sense to declare all of the fields initially as observables and then cluster them together into validatedObservables() ?
If at the end I want to submit the entire form, is there a smarter way of accomplishing this than concatenating each step using ko.toJSON(self.step1()). Would I need to create a "full form" observable that contains all of the input observables? In other words, what's the best way to serialize the full form? Would I want to use ko.toJSON(self) ?
What's the best way to reset the form to the initial configuration? Is there a way of re-applying ko.applyBindings(new myViewModel()) ?
Am I going about this correctly?
Thanks for any clarification.