Angular 2 is right around the corner, but that doesn’t mean you need to upgrade right away to start using web components. In fact Angular 1.5 provides all you need to start creating applications that will be relatively easy to upgrade to v2 when it finally comes out.

Everything is a Component

In Angular 2, and all the other web components ‘platforms’ out there for that matter, everything is a component. This concept sounds simple enough, it is very easy to say … everything is a component … right? Well it is not that simple to understand how deep this statement goes until you start working with it and realize that converting every view you have in your app to be made out of components is not as straightforward as you might think.

For example, you might have been using Angular for a couple of years. You think you have the entire framework figured out, you use views, add controllers to all of them, ui-router gives you nested views, you’ve added resolves to all your routes to feed them data, you might even be using interceptors and decorators. Well, how does all of that translate to components?

There is a very simple answer, it doesn’t.

We now want to create web components, as small and reusable as possible. Whenever you see a controller in your current app, probably means that particular spot can be broken into smaller components.

But how do we define components for the routes for example? Up until now we were declaring our states and adding templates from html files that point to a specific controller for the behavior. In the component tree architecture your view/state declarations also become components.

I tend to call these ones ‘router components’, they are usually dumb, might not even need a controller, and they only declare the layout for each state.

Let’s see an example, in your angular.config() block you are probably declaring your routes like this (note that I am using ES6):

// This class is imported in the main app.js file and used by angular.config();
export default class Config {
    constructor($routeProvider) {
        $routeProvider.
            when('/', {
                templateUrl: 'templates/home.html',
                controller: 'HomeController',
                controllerAs: 'HomeController'
             }).
            when('/orders', {
                templateUrl: 'templates/orders.html',
                controller: 'OrdersController',
                controllerAs: 'OrdersController'
            }).
            otherwise({
                redirectTo: '/'
            });
        }
}
Config.$inject = ['$routeProvider'];

Well in the new Angular 1.5 components, this should become something like:

// This class is imported in the main app.js file and used by angular.config();
export default class Config {
    constructor($routeProvider) {
        $routeProvider.
            when('/', {
                template: '<home title="The Home"></home>'
             }).
            when('/orders', {
                template: '<orders></orders>'
            }).
            otherwise({
                redirectTo: '/'
            });
        }
}
Config.$inject = ['$routeProvider'];

Pretty much the same, but no components flying around, and no html files being used as layouts. We are now using components instead, each of them will have a component declaration, maybe a controller if it needs one, and maybe some css if it needs them too.

In the example above, the home component will have two files put in a components/router/home subfolder:

import template from './home.html';

export class HomeController {
    constructor() {
        // If you inject anything into the controller, you will have to add it to this here
    }

    $onInit() {
        this.title = this.title || 'Default Title';
    }
}

const HomeComponent = {
    name: 'home', // name is not part of .component(), but I like to put it here
    bindings: {
        title: '@',
    },
    controller: HomeController,
    template,
};
export default HomeComponent;

That is the home.component.js file, and the template file:

<div class="home">
    <h2>{{ $ctrl.title }}</h2>
</div>

And finally we tell Angular that this is a component. I personally like to put all of these statements in the same app.js file as where I declare my angular module, like:

import HomeComponent from './components/router/home/home.component';
import OrdersComponent from './components/router/orders/orders.component';

const app = angular.module('myappname', [
    // dependencies
]);

app.component(HomeComponent.name, HomeComponent);
app.component(OrdersComponent.name, OrdersComponent);

There you go, now you have an app made in Angular that uses a ‘semi’ component tree architecture. Keep breaking up your fat views into smaller units, the smaller the better.

I myself have several components that I can already reuse between apps, for example a <time> tag that gets a format string and a date and displays it in that format. The format attribute is optional and defaults to the computer locale string if not specified.

I also have a <collapsible> panel that can have any content, and obviously expands or contracts reacting to some events, like clicking on the header.

Lifecycle Hooks

You might have noticed that in my home component I have a function called $onInit() that does the initialization of the title to a default value if it hasn’t been passed as an attribute to the component.

Angular 1.5 provides you with some component lifecycle hooks that you can use to handle specific situations. Here are their signatures and descriptions taken directly from the angular docs:

$onInit()

Called on each controller after all the controllers on an element have been constructed and had their bindings initialized (and before the pre & story linking functions for the directives on this element). This is a good place to put initialization code for your controller

$onDestroy()

Called on a controller when its containing scope is destroyed. Use this hook for releasing external resources, watches and event handlers.

$storyLink() 

Called after this controller’s element and its children have been linked. Similar to the story-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that the component can get an $element injected that will point to the actually root element of its template. Using $element inside storyLink() is the perfect place for all your DOM manipulation needs.

$onChanges(changes)

Called whenever one-way bindings are updated. The changesObj is a hash whose keys are the names of the bound properties that have changed, and the values are an object of the form { currentValue, previousValue, isFirstChange() }. Use this hook to trigger updates within a component such as cloning the bound value to prevent accidental mutation of the outer value.

Services and nothing else

After a while developing components instead of fat views, you realize soon enough that sometimes there is no easy way to communicate between them, like for example knowing the open or closed state of the side bar from the hamburger icon in the nav bar, or even closing the side bar from the content of the app when you click on the background.

To avoid this you should still use services, but that is it, nothing else.

Do you have interceptors? decorators? transformers? They should all be gone.

Yes you read right, get rid of them. I guarantee you there is an equivalent way of doing what those constructs do without having to use them. You can even create components with no template, only a controller with some logic to do what you need. Let’s say you need to add a custom header to your http calls? create a component called <http-handler /> (or whatever you want) that uses the $http service to call your endpoints, and use that component all over your application, just by adding it to your router components templates. In this component expose a couple of bindings to be able to pass it some configuration, and in its parent component handle the events that trigger the endpoint calls.

You might be thinking that this is too complicated, why would you do some convoluted web component to do that when you can just use a service and inject it into other components? Easy, because those components are not supposed to know which endpoints you are calling, or putting it differently, they should be decoupled from that service on the first place. Instead they should defer that behaviour to their parents.

Web Components are the future of web development, believe me. They are here to stay for a while and the sooner you stop thinking web development the classic way the better.

Thanks for reading.