-
Notifications
You must be signed in to change notification settings - Fork 379
Using Knockout Validation with Yeoman and generator ko
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.
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 });
});
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 };
});