#TECH

Validate and Restrict Input through Custom Data Bindings in KnockoutJS

KnockoutJS is an MVVM(Model-View-ViewModel) framework that is written in pure Vanilla JavaScript which makes it compatible with any mainstream browser.

KnockoutJS focuses on “Structured Coding” in the Front-End to make code more manageable in complex scenarios of Application. The way Knockout tackle this is by separating the Data from the View which changes dynamically as the View changes without any page reload.

We get many features like Automatic UI refresh, Dependency Tracking of the dynamic ViewModel, Bindings to bind Data from the ViewModel etc.

One of the feature that I will focus in this blog is creating Custom Bindings in Knockout.

A simple predefined Binding :

HTML

<body class="codeRunner">
  <label>Enter Input</label>
  <input data-bind="textInput: inputField, valueUpdate:'afterkeydown' ">
  
<p data-bind='text: inputField'>
  </p>
</body>

JavaScript

var ViewModel = function() {
  this.inputField = ko.observable();
}

ko.applyBindings(new ViewModel());

View in Fiddle : http://jsfiddle.net/rhn02o66/3/

Now lets create a Custom Binding.

There are two main properties that are enclosed within a custom Binding.

  • init: This event is triggered when the ViewModel is loaded that encapsulates the custom binding.
  • update: This event is triggered whenever a knockout observable to which binding is attached within the ViewModel is updated or modified.

A simple custom Binding that inherits the functionality of predefined Binding used in above snippet:

HTML

<body>
  <label>Enter Input</label>
  <input data-bind="limitInput: inputField, valueUpdate:'afterkeydown' ">
  
  <p data-bind='text: inputField'>
  </p>
</body>

Javascript

// custom binding
var allowedInput = function(length) {
  return {
    init: function(element, valueAccessor, allBindingsAccessor, bindingContext) {
      ko.bindingHandlers.textInput.init(element, valueAccessor, allBindingsAccessor, bindingContext);
    },

    update: function(element, valueAccessor) {
      var value = ko.unwrap(valueAccessor());
      if (value.length >= length) {
        valueAccessor()(value.slice(0, -1));
      }
    }
  }
};

ko.bindingHandlers.limitInput = allowedInput(6);

var ViewModel = function() {
  this.inputField = ko.observable('');
}

ko.applyBindings(new ViewModel());

View in Fiddle : http://jsfiddle.net/rhn02o66/6/

If you look at the HTML code you would see a new binding called limitInput (to limit the characters to 6) instead of regular textInput binding. Well, this is our custom binding which requires a function to understand the input and a way to initialize itself.

This required function is named as ‘allowedInput’ which returns a JavaScript object. The returning object has two events init for initialization and update to handle bindings as defined above. Now, let us look closely these two events.

The event init takes 4 parameters –

  • element stores the reference from which the binding is called.
  • valueAccessor has the value stored in parameter of the binding context, is an observable.
  • allBindingsAccessor reference to other bindings defined in ViewModel.
  • bindingContext is the functional context of the ViewModel from which it is evoked.

(For further Reference on passed parameters see http://knockoutjs.com/documentation/custom-bindings.html )

Further inside init event, the textInput binding is called which makes allowedInput to inherit all the objects and properties.

This allowedInput function is needed because we want to bind limitInput field so that it automatically inherits all the properties available to ‘textInput’ binding.

In update event, we have put a check on the length of the input-text due to which the length can not increase more than 6 character and in case it is greater than 6 character then this function will trim the extra characters and pass it to the valueAccessor().

In this way, we can create our custom binding according to our needs by also using the functioning of the predefined bindings.

Now let us look at some more examples in which the custom bindings can be used:

Custom binding for numeric input only:

HTML

<body>
  <label>Enter Numeric Input</label>
  <input data-bind="numericInput: inputField, valueUpdate:'afterkeydown' ">
  
  <p data-bind='text: inputField'>
  </p>
</body>

JavaScript

var allowedInput = function() {
  return {
    init: function(element, valueAccessor, allBindingsAccessor, bindingContext) {
      ko.bindingHandlers.textInput.init(element, valueAccessor, allBindingsAccessor, bindingContext);
    },

    update: function(element, valueAccessor) {
      var value = ko.unwrap(valueAccessor());
      if (isNaN(value)) {
        valueAccessor()(value.slice(0, -1));
      }
    }
  }
};

ko.bindingHandlers.numericInput = allowedInput();

var ViewModel = function() {
  this.inputField = ko.observable('');
}

ko.applyBindings(new ViewModel());

View in Fiddle : http://jsfiddle.net/rhn02o66/7/

In the above, limitInput is replaced by new custom binding called numericInput which takes numbers only as input and discard any other input as defined in its updateevent. Rest of the functioning is similar to what we described in our limitInput example.

Now, lets create a more generic binding using the above examples which will have the functionality of both custom bindings, limitInput, numericInput.

Generic binding for restricted input:

HTML

<body>
  <label>Enter Limited Numeric Input</label>
  <input data-bind="genericInput: inputField, valueUpdate:'afterkeydown' ">
  <label>Enter Limited Alpha Numeric Input</label>
  <input data-bind="alphaNumInput: alphaNumField, valueUpdate:'afterkeydown'">
  
  <p data-bind='text: inputField'></p>
  <p data-bind='text: alphaNumField'></p>
</body>

Javascript

var allowedInput = function(length, regex) {
  return {
    init: function(element, valueAccessor, allBindingsAccessor, bindingContext) {
      ko.bindingHandlers.textInput.init(element, valueAccessor, allBindingsAccessor, bindingContext);
    },

    update: function(element, valueAccessor) {
      var value = ko.unwrap(valueAccessor());
      if (value.length >= length || !regex.test(value.slice(-1))) {
        valueAccessor()(value.slice(0, -1));
      }
    }
  }
};

ko.bindingHandlers.genericInput = 
allowedInput(8, new RegExp("^[0-9\b]+$"));
ko.bindingHandlers.alphaNumInput = 
 		allowedInput(8, new RegExp("^[a-zA-Z0-9\b]+$"));


var ViewModel = function() {
  this.inputField = ko.observable('');
  this.alphaNumField = ko.observable('');
}

ko.applyBindings(new ViewModel());

View in Fiddle : http://jsfiddle.net/rhn02o66/8/

Continuing with what we learnt in the first two examples, the above example explains the usage of the Regular Expressions for limiting the input from the user.

Custom binding for email validation:

Regular Expression is a very powerful tool for string checking. One common way in which we can use Regular Expression is for Email validation. Combining it with the power of Custom Bindings we can create a custom binding for Email validations.

Lets dive into more technical details.

HTML

<body>
  <label>Enter Limited Numeric Input</label>
  <input data-bind="emailInput: emailField ">  
  <p data-bind='text: emailField'></p>
</body>

Javascript

var emailInput = {
    init: function(element, valueAccessor, allBindingsAccessor, bindingContext) {
      ko.bindingHandlers.textInput.init(element, valueAccessor, allBindingsAccessor, bindingContext);
    },

    update: function(element, valueAccessor) {
     var value = ko.unwrap(valueAccessor());
     var regex = new RegExp('^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$');
       if (value.trim() != '' && !regex.test(value)) {
         element.style.backgroundColor= '#EDB2B2';
       } else { 
         element.style.backgroundColor= 'white';
       }
     }
};

ko.bindingHandlers.emailInput = emailInput;

var ViewModel = function() {
   this.emailField = ko.observable('');
}

ko.applyBindings(new ViewModel());

View in Fiddle : http://jsfiddle.net/rhn02o66/9/

If the input email is invalid then the background of the input element will turn to red.

Custom bindings are pretty generic and can be applied to various use cases.

You might also like