In this post we are going to look at two very exciting ES6 features and how they can play a role in the context of an Angular application: Modules and Inheritance.
Once a module has been exported it can easily be imported from another file.
First things first: Inheritance is one of the most over abused software patterns of all times. If you aren’t family with the difference of Is-A and Has-A relationships, please take a moment and read it up. Similary, if you aren’t aware that you should favor composition over inheritance, please take a moment to read the linked article.
With ES6 we don’t need to rely on such non standard abstractions anymore. ES6 defines a few new keywords and syntax additions that allow for easier inheritance. What’s important to know is that it’s really only sugar on top of the good old prototypal inheritance model that we’ve been using for years.
To use inheritance we need to make use of the new
In the example above we construct a simple
Vehicle class which isn’t different from a simple constructor function in ES5. Where the new class syntax really shines is when you want to inherit from another constructor function. Let’s write a
Car class that inherits from
Seen that? We import
Vehicle as a module and extend it by using the new
extends keyword. We could have done the same with ES5 but it’s much more boilerplate code. Let’s forget about the nice module seperation for a moment and put both
Car into one file for the ES5 version.
Oh wow, things really got a lot easier with ES6, no?
Now that we know what modules and inheritance mean in the context of ES6, let’s take a look at how we can actually use it with Angular today.
Angular and ES modules
Let’s first look at ES6 modules in the context of Angular 1.x.
The spirit of Angular has always been to stay out of the way of the developer as much as possibles. It embraces simple POJOs instead of special Angular object types. With ES6 modules it’s even easier to excell on that idea. You can write your controller as a simple constructor function and have it exported as an ES6 module.
At some point though, we need to make Angular aware of the controller. Otherwise it just won’t play any role in our Angular application.
We simply import the
MainController in our
app.js file that we use to bootstrap our application. In order to register it as a controller, we pass it on to Angular’s
Angular and ES6 inheritance
The good news is, you already know how to use it! We’ve already seen how easy inheritance becomes with ES6 in our earlier example. Let’s create a
PageController and a
ProductPageController whereas the
PageController simply defines a
title() function that should be available in all controllers that derive from
PageController. All it does is that it prepends the string
Title: to the instance variable
While it’s possible to just set
_title to a string from within the constructor of our
ProductPageController we are aiming for the cleaner way and instead pass it to the constructor of our
PageController by calling
super('ES6 inheritance with Angular');
That’s probably not the most exciting example in the world but it works! All there’s left to do is to angularize the
Please note that we don’t have to do the same with the
PageController as long as it’s not explicitly used as an Angular controller. In our case, it’s only used implicitly by the
Edit: Evgeniy asked on G+:
“how can be ‘title’ argument in constructor of next controller be legit? It doesn’t look like the name of service”.
The parameters in our constructor do work together with Angulars DI. In fact, the constructor is not different from a traditional constructor function. But how can
title be legit then? The reason for that to work is that the
title parameter is only used by the
PageController which isn’t registered with
PageController is only used implicitly by the
Easy isn’t it? Can we use that for services, too? Yes, we can but there’s a small gotcha. It doesn’t work with services that are defined using the
myModule.factory(fn) API but only for those that are defined using
myModule.service(fn). That’s because services that are defined using the
myModule.service(fn) API are instantiated with the
new operator under the hood whereas the others are not. For inheritance to work it’s important that our constructor function is instantiated with
There’s one more gotcha pointed out by Evgeniy: When we use ES6 classes we lose the ability to use explicit dependency annotation with the inline array notation.
In order to preserve dependency annotations for minification, we need to use the
$inject property notation now:
MainController.$inject = ['SearchService'];
Getting started with our boilerplate
There are plenty of different ways to get started with ES6 today. The sheer amount of different ways to approach it can be very confusing. At thoughtram we created a boilerplate that makes it quite easy to get rolling. It uses the popular babel transpiler to convert ES6 to ES5 code that works in all current major browsers. The boilerplate also uses browserify to concat and minify all ES6 modules into a single file.
Exploring Angular 1.5: Lifecycle Hooks
Angular 1.5 is finally out! This article discusses the new lifecycle hooks in Angular.
Sponsoring AngularConnect. Again.
Today we are very happy to announce that we're going to sponsor AngularConnect - again! We're also going to run...
ngMessageFormat - Angular's unheard feature
Angular 1.5 is pretty much around the corner. It turns out that there's a feature that already landed in Angular...
Multiple Transclusion and named Slots
One of those bigger features in the 1.5 release is multiple transclusion via named slots. In this article we're going...
Service vs Factory - Once and for all
This is yet another article on services vs factories in AngularJS. It explains once and for all, why we mostly...
Taking Angular Master Class to the next level
We always collect feedback to make our material and trainings even better. We listened. Here's what we did.