Writing AngularJS Apps Using ES6

As many of you are aware, ECMAScript 6 is in its draft state now and is expected to be finalized some time this year. But it has already caught a lot of attention in the community and browsers have already started implementing it. We also have a number of transpilers like Traceur, 6to5, and many others that convert ES6 code to ES5 compatible code. Community members have started playing around with ES6 and many of them are blogging about what they learn. SitePoint’s JavaScript channel also has a good number of articles describing the different features of ES6.

It is possible to write any piece of everyday JavaScript using ES6. To do this, we need to be aware of the key features of ES6 and know which piece fits where. In this article, we will see how we can use features of ES6 to build different pieces of an AngularJS application and load them using ES6 modules. We will do this by building a simple online book shelf application and we will see how it is structured and written.

A Note on the Bookshelf Application

Home page: Shows a list of active books. Books can be marked as read and moved to the archive from this page

Add book page: Adds a new book to the shelf by accepting the title of the book and name of the author. It doesn’t allow a duplicate title

Archive page: Lists all archived books

Setting up the Application for ES6

As we will be using ES6 to write the front-end part of the application, we need a transpiler to make the ES6 features understandable for all the browsers. We will be using the Traceur client-side library to compile our ES6 script on the fly and run it in the browser. This library is available on bower. The sample code has an entry for this library in bower.json.

On the home page of the application, we need to add a reference to this library and the following script:

The app’s JavaScript code is divided into multiple files. These files are loaded into the main file using the ES6 module loader. As today’s browsers can’t understand ES6 modules, Traceur polyfills this feature for us.

In the sample code, the bootstrap.js file is responsible for loading the main AngularJS module and manually bootstraping the Angular app. We cannot use ng-app to bootstrap the application as the modules are loaded asynchronously. This is the code contained in that file:

Here, bookShelfModule is name of the AngularJS module containing all the pieces. We will see the content of the bookShelf.main.js file later. The bootstrap.js file is loaded in the index.html file using the following script tag:

<script type="module" src="ES6/bootstrap.js"></script>

Defining Controllers

AngularJS controllers can be defined in two ways:

Controllers using $scope

Using the controller as syntax

The second approach fits better with ES6, as we can define a class and register it as a controller. The properties associated with an instance of the class will be visible through the controller’s alias. In addition, the controller as syntax is comparatively less coupled with $scope. If you are not aware, $scope will be removed from the framework in Angular 2, so we can train our brains to be less dependent on $scope from now on by using the controller as syntax.

Though classes in ES6 keep us away from the difficulty of dealing with prototypes, they don’t support a direct way of creating private fields. There are some indirect ways to create private fields in ES6. One of them is to store the values using variables at module level and not including them in the export object.

We will use a WeakMap to store the private fields. The Reason behind choosing WeakMap is that those entries that have objects as keys are removed once the object is garbage collected.

As stated above, the home page of the application loads and displays a list of active books. It depends on a service to fetch data and to mark a book as read, or to move it to the archive. We will create this service in the next section. So that the dependencies injected into controller’s constructor are available in instance methods, we need to store them in the WeakMaps. The home page’s controller has two dependencies: the service performing the Ajax operations and $timeout (used to show success messages and hide them after a certain time). We also need a private init method to fetch all active books as soon as the controller loads. So, we need three WeakMaps. Let’s declare the WeakMaps as constants to prevent any accidental re-assignment.

The following snippet creates these WeakMaps and the class HomeController:

As you see, there is no difference in the way that we applied dependency injection — it is same as the way we do in ES5. We are exporting the HomeController class from this module.

Check the code of AddBookController and ArchiveController. They follow a similar structure. The file bookShelf.controllers.js imports these controllers and registers them to a module. This is the code from this file:

The bookShelf.controllers module exports the name of the AngularJS module it created, so that this can be imported into another module to create to create the main module.

Defining Services

“Service” is an overloaded term in general and in Angular as well! The three types of services used are: providers, services and factories. Out of these, providers and services are created as instances of types, so we can create classes for them. Factories are functions that return objects. I can think of two approaches for creating a factory:

The same as in ES5, create a function which returns an object

A class with a static method which returns an instance of the same class. This class would contain the fields that have to be exposed from the factory object

Let’s use the second approach to define a factory. This factory is responsible for interacting with the Express API and serving data to the controllers. The factory depends on Angular’s $http service to perform Ajax operations. As it has to be a private field in the class, we will define a WeakMap for it.

The following snippet creates the factory class and registers the static method as a factory:

This snippet uses the following additional features of ES6 (in addition to classes and arrow functions):

A static member in the class

String templates to concatenate the values of variables into strings

Defining Directives

Defining a directive is similar to defining a factory, with one exception — we have to make an instance of the directive available for later use inside the link function, because the link function is not called in the context of the directive object. This means that the this reference inside the link function is not the same as the directive object. We can make the object available through a static field.

We will be creating an attribute directive that validates the title of the book entered in the text box. It has to call an API to check if the title exists already and invalidate the field if the title is found. For this task, it needs the service we created in the previous section and $q for promises.

The following snippet creates a directive which it registers with a module.

Conclusion

Hopefully this gives you an insight into using ES6 to write AngularJS apps. AngularJS 2.0 is being written completely using ES6 and as web developers we need to be aware of the way we have to write our code in the near future. ES6 solves many problems that have been bugging JavaScript programmers for years and using it with AngularJS is a lot of fun!

Rabi Kiran (a.k.a. Ravi Kiran) is a developer working on Microsoft Technologies at Hyderabad. These days, he is spending his time on JavaScript frameworks like Angular JS, latest updates to JavaScript in ES6 and ES7, Web Components, Node.js and also on several Microsoft technologies including ASP.NET 5, SignalR and C#. He is an active blogger, an author at SitePoint and at DotNetCurry. He is rewarded with Microsoft MVP (ASP.NET/IIS) and DZone MVB awards for his contribution to the community.