Skip to content

Using Knockout Validation with Yeoman and generator ko

Emlyn Clay edited this page May 26, 2015 · 1 revision

You may be using Yeoman scaffolding your application and have chosen to use the excellent generator-ko from Steve Sanderson. This is a guide to using knockout-validation with the generator-ko yeoman generator.

Initialise knockout-validation in startup.js

Your application is likely to be structured into multiple components and if that is the case it would make sense to initialise the knockout-validation in startup.js so that it is create only once, remember to include knockout-validation in the define() function so that the dependency is injected.

define(['jquery', 'knockout', './router', 'bootstrap', 'knockout-projections', 'knockout-validation'], function($, ko, router) {

  ... // component registrations

  // [Scaffolded component registrations will be inserted here. To retain this feature, don't remove this comment.]

  // Start the application

  // initialise validation
  ko.validation.init(); // <--- initialises the knockout validation object

  ko.applyBindings({ route: router.currentRoute });
});

Exemplar component - a login form

This is a working example of how to use Knockout-Validation in a login component. In this example, the template contains two fields username and password that are bound to the username and password fields of the Login ViewModel. The template will only show errors is the Login.errors observableArray has something in it (i.e. errors().length > 0) and the button of the form is bound to the Login.login() method.

The code in login.js demonstrates how you use the validation extension method to provide validation instructions and note that the model is validated by calling var errors = ko.validation.group(self); — this is important! You are calling validation on the Login ViewModel instance. errors() returns an array of all the validation messages and in this example they update Login.errors(). This avoids signalling a re-write until you plan to display the errors. username field has a custom message and it's there to demonstrate how to modify the validation output.

Create a new component:

yo ko:component login

Then in components/login.html, insert the following:

<h1>Login</h1>
<div class="col-sm-12">
  <div class="row" id="login-form">

    <!-- Validation messages -->
    <div class="error-messages" data-bind="visible: errors().length > 0, foreach: errors">
      <div class="alert alert-danger" data-bind="text: $data">
      </div>
    </div>

    <form>
      <div>
      <label>Username</label>
      <input type="text"
             class="form-control"
             id="username"
             value=""
             data-bind="value: username"
             placeholder="Username"/>
      </div>
      <div>
      <label>Password</label>
      <input type="password"
             class="form-control"
             id="password"
             value=""
             data-bind="value: password"
             placeholder="Password"/>
      </div>
      <input type="checkbox" id="remember_me"  value="remember-me" >
      <label for="remember_me" >Remember Me</label>
      <button id="btn-login" class="form-control" data-bind="event: {click: login}"><b>Login</b></button>
    </form>
  </div>
</div>

Then in components/login.js, insert the following:

define(['knockout', 'text!./login.html', 'knockout-validation'], function(ko, templateMarkup, validation) {

    function Login(params) {
        // hold a reference to the parent 'this'
        var self = this;

        // properties
        self.username       = ko.observable('').extend({
            required: {
                params: true,
                message: 'Please supply your username'
            }
        });

        self.password       = ko.observable('').extend({required: true});
        self.token          = ko.observable(null);
        self.isLoggedIn     = ko.observable(false);
        self.errors         = ko.observableArray([]);

        // methods
        self.login = function() {

            // validate this model
            var errors = ko.validation.group(self);

            // if there are any validation errors, then the view will update to
            // tell the user, else proceed to login and reroute the user
            if (errors().length === 0) {

                // login using the api
                login = true;

                if(login) {

					self.token('TOKEN-123');
                    alert('User is logged in!');

                } else {
                  // update the model if there are errors
                  self.errors(errors());
                  return false;
                }
            } else {
              // update the model if there are errors
              self.errors(errors());
              return false;
            }

    }


    // This runs when the component is torn down. Put here any logic necessary to clean up,
    // for example cancelling setTimeouts or disposing Knockout subscriptions/computeds.
    Login.dispose = function() { };

    return { viewModel: Login, template: templateMarkup };

});