an introduction to knockout.js

An Introduction to Knockout.js Form Tracking and Validation

This blog post was written by and published 23rd July 2017.

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.

// this code is used in form example below to display input data
<span data-bind="text: firstName"></span>

Example form:


Input copy will be shown here once the input is blurred:

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.

Example Form



Welcome:

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")
  }
}

Out 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


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!"
  }
})

Lets try this again:

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 ‘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',
});