How to write nice angular components in ES2015

Angular

Here at Zenchef, our app is mainly in Angular 1.x, with some features in React (thanks ngReact).
Getting back to Angular after a project on React is not easy, especially if you have an old codebase in ES5. Did you know that with a little work you can get some of that React component sweetness with Angular’s own component system ?

Well, if not, let’s discover how !

Why Angular components ?

Why not using directives ? Well first, directives are really a pain to write while components are way easy to work with on that point. Moreover, native imports are easier to use with components, especially if you want to work with imported ES2015 classes.

class Directive {

constructor($rootScope, myFactory) {
this.rootScope = $rootScope
this.service = myFactory
}

link(scope, element, attrs) {
this.foo = ""
let method = () => {
this.service.get().then((data) => {
this.foo = data
})
};
}

static directiveFactory($rootScope, myFactory){
Directive.instance = new Directive($rootScope, myFactory)
return Directive.instance
}
}

Directive.directiveFactory.$inject = ['$rootScope', 'myFactory'];
export { Directive }

Then you register an instance of your directive to your angular module (you can also do a new instance with new () )

import MyFactory from './MyFactory'
import {Directive} from './Directive
angular.module('app', [])
.factory('myFactory', MyFactory)
.directive('myDirective', Directive.directiveFactory)

VS components

import ctrl from './MyCtrl'
import temp from './MyTemplate.html'

export default {
bindings: {
'user':'=',
'source':'='
},
template: temp,
controller: ctrl,
controllerAs: 'vm'
}

Registering is easy, no instance needed

import MyComponent from './MyComponent'angular.module('app', [])
.factory('myFactory', MyFactory)
.component('myComponent', MyComponent)

Note that with directive, you still have the link property (and all the other if you want), even with a ES2015 class while with component you simply bind a controller or, in that case, a simple class.
One other good thing with components is that they use the controllerAs syntax by default ($ctrl by default, but you can change the prefix by adding a controllerAs prop to the component, as seen above.) and the bindToController is set by default.

Architecture

Now that we’ve seen how simple Angular components are, let’s talk about the mean of it : the template and controller. As you can see above, two files were imported :

import ctrl from './MyCtrl'
import temp from './MyTemplate.html'

What’s inside them ?

MyTemplate.html

<div>
<h3>{{vm.title}}</h3>
<a>{{vm.user.name}} from {{vm.source.name}}</a>
</div>

MyCtrl.js

export default class MyCtrl {
constructor($timeout, myFactory) {
this.timeout = $timeout
this.service = myFactory
}

$onInit() {
this.title = "A title"
this.getBindings()
}

getBindings = () => {
console.log(this.user)
console.log('---')
console.log(this.source)
}
}

MyCtrl.$inject = ['$timeout', 'myFactory]

Well, that’s not totally finished but we’ll work with it.
Note the $onInit() method, with angular 1.6+ you must use it to do your initial bindings.

Now, we have our three files : MyTemplate.html, MyCtrl.js and MyComponent.js.
MyComponent imports the other two but how do you import MyComponent.js ? You could do it directly where you initialize your Angular module but a nice way to import your components could be to treat all of them as a collection and pick the ones you want. Exactly like you would do with React-router for exemple.

Create a /components folder, create a /MyComponent folder inside and put all three files inside.
Now at the root of /components you can create a index.js file like so :

import _MyComponent from './MyComponent/MyComponent'
export { _MyComponent as MyComponent}

import _MyComponent2 from './MyComponent2/MyComponent2'
export { _MyComponent2 as MyComponent2}

import _MyComponent3 from './MyComponent3/MyComponent3'
export { _MyComponent3 as MyComponent3}

import _MyComponent4 from './MyComponent4/MyComponent4'
export { _MyComponent4 as MyComponent4}

And the registration :

import {MyComponent} from './components'
import {MyComponent2} from './components'
angular.module('app', [])
.factory('myFactory', MyFactory)
.component('myComponent', MyComponent)
.component('myComponent2', MyComponent2)

Thats extremely useful, especially if you have modules to lazyload (and you should!) and multiple module initialization through your app

So…

I love React and I love components ! I think with this method using ES2015 features like classes and import you can really work with something a little similar in Angular. I started to love Angular again since I began to use ES2015 and some best practices (like less $scope, the better) and I really hope this little article will help you love it (again) too.

Have fun coding!

--

--

Ashley Romain Bonhomme
Zenchef’s Tech and Product Blog

Romain is an enthusiast javascript developer. He’s also into PC gaming and cats (especially Scottish folds)