An Introduction to Knockout.js Form Tracking and Validation

Simplify dynamic JavaScript UIs with Model-View-View Model (MVVM)
Knockout.js is a powerful JavaScript framework which allows you to easily bind website (DOM) elements to the underlying data model. It uses observers to make your UI automatically stay in sync with an underlying data model, along with a powerful and extensible set of declarative bindings to enable productive development.
Benefits of using Knockout.js
- Greatly simplifies synchronization between the client UI and server
- Leverages MVVM design pattern to increase modularity and provide for a clean separation of concerns and cohesive implementations.
- Keeps UI model state management on the client side
- Decreases (potentially) the size of server responses and client/server traffic in general (thereby speeding up our apps)
About Knockout.js:
- Knockout.js was developed and is maintained by Microsoft employee; Steve Anderson
- The first stable version was released in November 2015
- When minified and gzipped the Knockout.js file is only 22kb in size
What is Model View View-Model (MVVM)
MVVM pattern in Knockout is used to create a UI specific ViewModel that is always perfectly in sync with the UI DOM elements used to display, update, delete, or create data in the ViewModel.
This means that with relatively little coding, you dont have to worry about losing data that has been modified, added, or removed by client interaction. The data in the ViewModel is constantly tracked and updated without further developer involvement.
Components of MVVM
- Model The Model represents the actual data and/or information we are dealing with. An example of a model might be a contact (containing name, phone number, address, etc.)
- View The View is the front-end side and manages how the Model data is presented, the View contains behaviours, events, and data-bindings and is synchronised to the ViewModel which will update the UI.
- ViewModel The ViewModel keeps the Model seperate from the View, for example: the Model may contain a contact, the View will contain the formatted contact information (name, number, address etc) and the ViewModel will handle changes to the Model and update the View accordingly.
Install Knockout.js:
You will first need to download Knockout.js or link to a CDN version.
Once you've linked to your Knockout.js file you can set up your KO (Knockout) ViewModel and initialise KO.
// SETUP knockout VIEW MODEL
function myKnockoutVM() {
// bind knockout to this
var self = this;
// Knockout bindings/functions will go here
}
// INITIALISE Knockout
ko.applyBindings(new myKnockoutVM());
Now Knockout is ready to start binding to your UI.
Knockout Form Binding
Step 1: Simple Input Bindings:
Knockout can be used to track input values and update UI using this information
First, we need to create our KO bindings, we're going to create an observable to store data for a first name input. We will use this observable later to display the user's name on screen.
// SETUP knockout VIEW MODEL
function myKnockoutVM() {
// bind knockout to this
var self = this;
// setting up our firstName observable
self.firstName = ko.observable();
}
// INITIALISE Knockout
ko.applyBindings(new myKnockoutVM());
Listen for input updates With our firstName observable setup we can create an input to bind to our observable.
We link our text inputs to their KO observables using the 'data-bind' property.
<label>First Name:</label>
<input type="text" data-bind="value: firstName"/>
Using the 'value' binding will update the View Model on blur, this can be changed to 'textInput' to track on keydown.
Displaying value of input Now we have our first name observable tracking the first name input we can output this data on the page.
To do this we use the 'data-bind' property again but instead of using the bindings 'value' or 'textInput' we use text which tells Knockout to output the data into this element as text.
Step 1 Summary
We can now create a Knockout observable and bind this to our View Model using an input with 'value' or 'textInput' data-bind types and use this data to update the user interface using the 'text' data-bind type.
Step 2: Computed Input Bindings:
Computed observables can be used much like functions, to combine observables or other data to return a value.
First, we'll add another observable which will be used to store data for the user's surname.
// SETUP Knockout VIEW MODEL
function myKnockoutVM() {
// bind Knockout to this
var self = this;
// setting up our firstName and surName observables
self.firstName = ko.observable("");
self.surName = ko.observable("");
}
// INITIALISE Knockout
ko.applyBindings(new myKnockoutVM());
Once we have the surName observable setup we can create a computed observable to combine the firstName & surName observables and output the user's fullname.
// SETUP Knockout VIEW MODEL
function myKnockoutVM() {
// bind Knockout to this
var self = this;
// setting up our firstName and surName observables
self.firstName = ko.observable("");
self.surName = ko.observable("");
// new computed observable to combine firstName and surName observables
self.fullName = ko.computed(function(){
return self.firstName() + " " + self.surName()
})
}
// INITIALISE Knockout
ko.applyBindings(new myKnockoutVM());
The fullName observable can now be used to display the users full name by returning the firstName / surName observables with a space in the middle.
Note: We're using the 'value' binding for the first name input and the 'textInput' binding for the second name input, see how these update the fullName observable differently.
Step 2 Summary
Computed observables can be useful for keeping bloated html to a minimum and returning calculated values (maths etc), they can be used like any Javascript function and can include if statements to return different values as needed.
Knockout Form Validation
Installing Knockout.js validation
Knockout.js can be used for validating forms using simple or complex validation rules, to do this you will first need to download or link to the minified version, available here.
We'll cover some basic and custom validation rules below, but for more information you can have a look at the GitHub page for Knockout Validation.
Basic validation
Knockout Validation can be used for simple validation rules such as:
- Field required
- Field must have min/max lengths
- Field must be valid email address
- Field Input min/max number (e.g number must be higher than X)
- Field must equal (e.g. for use with captcha or similar)
To track validation errors we need to create and new Knockout object to hold our errors and validate against our rules, to do this we add a simple line:
self.errors = ko.validation.group(this);
With this setup, we can check this object and look to see if there is any errors in our form.
To check for errors we'll create a simple function which will be triggered on a button click event
self.submit = function(){
if(self.errors().length > 0){
console.log("Validation Failed")
self.errors.showAllMessages();
} else {
console.log("Validation Passed")
}
}
Our button is going to use a 'click' data-bind event to trigger our function on click, see below:
<button data-bind="click: submit">Submit</button>
Now when this button is clicked it's going to check our validation rules and if any errors are found then it will console log "Validation Failed" and then show our error messages using the built-in 'showAllMessages()' function.
Getting started
To add validation to an observable we add '.extend({})'
to the end of our observable code, like below:
self.firstName = ko.observable("").extend({});
We then add our validation rules inside the 'extend' function.
Field required validation
This is the most simple type of validation and one which is used on many of our application forms.
To make a field required we just need to add 'required: true' inside our extend function, as below:
self.requiredValue = ko.observable("").extend({
required: true
});
Lets see what this does This field is required, but let's leave its empty and hit the submit button Submit
When we try and submit we get an error message 'This field is required', typing in the field will resolve the error and the message will be hidden, but what if we want our own custom validation message?
To add own own custom error message we can edit the 'required' line and further extend this, like so:
self.requiredValue = ko.observable("").extend({
required: {
message: "Don't forget this question!"
}
})
Other built-in validation methods
We can also use the following validation methods as show: Email validation
self.email = ko.observable("").extend({
email: true
})
or
self.email = ko.observable("").extend({
email: {
message: "Please check you've entered your email correctly"
}
})
Min/Max length validation We can check fields have a minimum or maximum input length easily with Knockout, these are useful for validating fields such as user's name, email address etc.
self.length = ko.observable("").extend({
minLength: 3,
maxLength: 40,
})
This will flag an error if user input is less than 2 characters or more than 40 characters long, again its easy to add our own custom messages for each:
self.length = ko.observable("").extend({
minLength: {
params: 3,
message: "Please enter more than 3 characters"
},
maxLength: {
params: 40,
message: "Please enter less than 40 characters"
}
})
Min/Max number validation We can validate that the minimum value of an input is more than or less than set numbers in the same way we check min/max lengths.
self.length = ko.observable("").extend({
min: 100,
max: {
params: 25000,
message: "This number is too big!"
}
})
The code above will flag an error if the number entered into input is less than 1,000 or more than 25,000.
Custom validation rules
Now we've learnt how to take advantage of Knockout Validation's built-in validators we can look at building our own custom validators, for complex fields and requirements.
Knockout custom validators work in the same was as a simple JavaScript if statement will work, we check the value of the data and return either a true (validation passed) or false (validation failed) booleen.
To create a custom validation rule we create a new extension object 'validation: {}', this will hold our validation function and also our custom message.
A simple custom validation script would looks like this:
self.customValidatedObservable = ko.observable("").extend({
validation: {
validator: function (inputValue) {
if (inputValue) {
return true
} else {
return false
};
},
// our custom message
message: 'You've missed this question',
}
});
This simple validation rule does the same as the built-in required: true
validator, checking the inputValue
exists and if it does the function will return true and validation is passed, if inputValue
is empty then validation will fail and the message 'You've missed this question' will be displayed.
Another simple custom validator could check that the user's age is between 18-60:
self.ageValidated = ko.observable("").extend({
validation: {
validator: function (age) {
if (age >= 18 && age <= 60) {
return true
} else {
return false
};
},
// our custom message
message: 'Sorry, but you must be aged between 18 and 60.',
}
});
These validation rules have been build directly into our observable and can only be used on this observable (without copying/pasting to each), this is useful when you know the validation method is only required once. Validation rules directly added to an observable are technically called 'Anonymous' or Single-Use Custom Rules.
We can also create Custom Rules to be used globally within our ViewModel, these validators are used in a similar way to the built-in validation methods e.g. 'validatorName: parameters', and reduce on the amount of code replicated for similar inputs.
ko.validation.rules\['betweenAmount'\] = {
validator: function (val, otherValue) {
return val >= ko.validation.utils.getValue(otherValue\[0\]) && val <= ko.validation.utils.getValue(otherValue\[1\]);
},
message: 'This value must be between {0} and {1}',
};
When using Custom Rules like the 'betweenAmount' checker above we need to register our rules using the following code:
// when using Custom Rules you need to include the code below to register these custom rules
ko.validation.registerExtenders();
We can then use this rule for multiple input fields without duplicating the code on each observable.
**See full example below**
ko.validation.rules\['betweenAmount'\] = {
validator: function (val, otherValue) {
return val >= ko.validation.utils.getValue(otherValue\[0\]) && val <=
ko.validation.utils.getValue(otherValue\[1\]);
},
message: 'This value must be between {0} and {1}',
};
// register custom rules
ko.validation.registerExtenders();
// SETUP Knockout VIEW MODEL
function myKnockoutVM() {
var self = this;
// observables to track amount between two numbers
self.someAmount = ko.observable().extend({
// this field must be between 1 and 500
betweenAmount: \[1, 500\]
});
self.someOtherAmount = ko.observable().extend({
// this field must be between 1500 and 3000
betweenAmount: \[1500, 3000\]
})
}
// INITIALISE Knockout
ko.applyBindings(new myKnockoutVM() );
Validation options
Knockout Validation includes some options to help you customise your validation rules, these give you the ability to choose if/what error classes are applied to the invalid input as well as choose error message class so you can style error messages as you need.
All options are listed below with explanations of what they do.
// global KO validation configuration
ko.validation.init({
// register custom single-use rules defined via ko.validation.rules registerExtenders: true,
// NOTE: this seems to work best when not included in this options object but separately like I've shown in the Custom Rules above: ko.validation.registerExtenders();
// indicates whether validation messages are triggered only when properties are modified or at all times – definitely recommend setting this to 'true'
messagesOnModified: true,
// this controls whether error messages should be automatically added or added using the 'ValidationMessage: observable()' data-bind type.
// set to false if including radio buttons (error messages break button layout)
insertMessages: true,
// indicates whether to assign an error class to the <input> tag when your property is invalid.
decorateInputElement: true
// indicates whether css error classes are added only when properties are modified or at all times – definitely recommend setting this to 'true'
decorateElementOnModified: true,
// class applied to invalid inputs
errorElementClass: 'input-error',
// class used on error messages
errorClass: 'message-error',
});