Part 2: Component Router Lifecycle Hooks
& Using ES6 classes in Angular 1.x
In part 1 we looked at how router conventions help you stay organized and write less code.
Note: Component router is still in development & only available for Angular 1.3+
Now we'll look at something much more awesome. Using a multi-step form as an example, we'll see why you should take advantage of ES6 classes in Angular, and look at how to use component routers Lifecycle Hooks.
Project
Here's the scenario: you have a multi-step user sign-up form, and you want to save the user data whenever there is a route change.
First we'll need a service to save the data.
ES6 Classes as Services
This service will save our user form data to UserService.user.
We can use UserService.save(updates)
to merge new form data objects with our user. For example, if our user has existing data, {name: 'Joe'}
, we can push more updates {age: 24}
to make a complete user object {name: 'Joe', age: 24}
.
class UserService {
constructor() {
this.user = {};
}
save(updates) {
function update(obj) {
for (var i = 1; i < arguments.length; i++) {
for (var prop in arguments[i]) {
var val = arguments[i][prop];
if (typeof val === 'object') {
update(obj[prop], val);
} else {
obj[prop] = val;
}
}
}
return obj;
}
update(this.user, updates);
// a Promise could be used here, returning false if there is an error.
return true;
}
}
export default angular.module('app.services.user', [])
.service('UserService', UserService);
ES6 Classes as Controllers
Why use ES6 classes as controllers? Inheritance.
But first, let's make our UserFormCtrl and inject the UserService we just made.
export class UserFormCtrl {
constructor(UserService) {
this.user = UserService;
// bind ng-model to this.user
}
}
UserFormCtrl.$inject = ['UserService'];
This is what we want all our User Form controllers to inherit from.
LifeCycle Hook: canDeactivate
The Component router has a cool new feature called lifecycle hooks. These are functions that can be run during phases of routing, they include:
- canActivate
- canDeactivate
- deactivate
- activate
I'll encourage you to read more about these hooks, but for the sake of brevity, let's focus on what we need.
We want the form to save before the route changes, so we're looking at the canDeactivate hook.
Let's adjust our FormCtrl class.
UserFormCtrl.prototype.canDeactivate = function () {
return this.User.save(this.form);
// true = continue
// false = stay with the current route. change fails.
};
Now we have a class UserFormCtrl that will save on route change to our UserService.
Extending an ES6 Class
We can now create a series of multi-step form views with corresponding controllers.
class UserProfileCtrl {}
class UserDescriptionCtrl {}
class UserGoalsCtrl {}
//
export default angular.module('app.user.form', [])
.controller('UserProfileCtrl', UserProfileCtrl)
.controller('UserDescriptionCtrl', UserDescriptionCtrl)
.controller('UserGoalsCtrl', UserGoalsCtrl);
These form controllers can gain the ability to save to UserService through inheritance.
class UserProfileCtrl extends UserFormCtrl {}
class UserDescriptionCtrl extends UserFormCtrl {}
class UserGoalsCtrl extends UserFormCtrl {}
In the view, bind your forms to the model this.user
from UserFormCtrl and everything should work. Forms will be saved on route changes.
<input type="text" ng-model="vm.user.password">
<a ng-link="userDescription">
<button>Next</button>
</a>
<!-- note: ng-link requires the a tag -->
Conclusion
There are good reasons to use ES6 Classes for controllers and services rather than just functions. It's not just a style decision.
Also bear in mind, this is only scratching the surface of what you can do with the Component router's lifecycle hooks. These hooks are a great reason to try out the new router today.