Migrating AngularJS Directives To Aurelia – Part I: Custom Elements

So you’ve decided it’s time to join the future and port your existing Angular application to Aurelia. Your first port of call was probably the documentation and chances are you probably got confused, because Aurelia lacks the concept of a “directive” – don’t be discouraged my friend, there is light at the end of the tunnel.

The concept of directives do exist in Aurelia, however they go by the names: Custom Attribute and Custom Element. In Angular 1.x, a directive can both be a custom attribute and a custom element. It is a confusing concept that fortunately has been removed in Angular 2.

If you’re looking to jump the fence to greener pastures into Aurelia land, then this article will show you how to port over Angular directives to Aurelia.

Part I: Custom Elements

In AngularJS 1.x a custom element is created using what is called a directive. In Aurelia a directive is either a custom element or custom attribute. The terminology is different, Aurelia’s is more concise and explanatory in comparison to Angular’s use of the word directive.

In this section we are going to create a custom HTML element which is used using the HTML element. We will also have the ability to pass through an object of data which the custom element will use to populate itself.

Angular

As we discussed earlier, custom elements in Angular 1.x are directives with the restrict property set to ‘E’ — lets proceed.

Create the directive

Firstly, lets define our custom element in a file we will aptly call person-directive.js – this will be responsible for setting up what type of directive we are creating and what data it can accept.

angular.module('myApp', [])
    .directive('person', function() {
        return {
            restrict: 'E',
            scope: {
                person: '=data'
            },
            templateUrl: 'person-directive.html'
        }
    });

Create the directive view template

Although Angular directives allow us to create inline templates, to keep in the spirit of doing things the way we can do them in Aurelia, we will be creating a separate view template called person-directive.html


    Name: {{person.name}}, Location: {{person.location}}

Using our directive

As you can see we have our custom element created. We can reference it using and it if the passed in data matches what we expect in the view, we will see their name and location.

Create a controller (aka ViewModel)

This controller handles passing through data to a specific part of our application. You might access this controller through a route called “/people” if this were a real application. In this controller we define some mock data, an array of people objects.

angular.module('myApp', [])
    .controller('PersonController', function($scope) {
        $scope.people = [
            {name: 'Dwayne', location: 'Australia'},
            {name: 'Rob', location: 'USA'},
            {name: 'Max', location: 'France'}
        ];
    });
Create a controller view (aka View template)

This might not reflect how you would render the above controller in the real world, but for our purposes it will work just fine. Presumably here we would be including Angular itself from a CDN, our directive and controllers.


    
        PeopleApp
    
    
        
            
                
                    
                
            
        
    

As you can see, Angular is a horrible mess. Some concepts kind of make sense, especially if you’re drunk or high. But honestly, concepts such as $scope and restrict are constant sources of confusion and frustration for newcomers.

Aurelia

Now we are going to create the same custom element within Aurelia. In Aurelia custom elements are referred to as custom elements, so no confusing double-meaning directive terminology here.

One of the first things you might notice is we have considerably less code in comparison to the above example.

Create the custom element

The below example is a little more verbose. We can also specify an element as a custom element or attribute by naming the class PersonCustomElement and remove the @customElement decorator if we wanted to shave off an extra line of code.

We are assuming this file is called person-element.js (this is important as we will import this later using this name).

import {customElement, bindable, bindingMode} from 'aurelia-framework';

@customElement('person')
@bindable({ name: 'person', attribute: 'data', defaultBindingMode: bindingMode.twoWay})
export class Person {
}

Create the custom element view template

You might have noticed that we didn’t specify a view template name above. Because Aurelia favours a convention based approach, it assumes a ViewModel has a paired View of the name filename, just with a .html extension instead.

Using the above mentioned convention, we call this file person-element.html and leave it to Aurelia to pair them up.

Using our custom element

We use our custom element the same way we used it above in our Angular example. We still reference our element using inside of our View. This is the only comparable feature between Angular and Aurelia in how something works.

Create a ViewModel (aka controller)

In Aurelia the concept of a controller does not exist. Everything utilises ViewModel’s which are presumed to be paired with a View template (using default conventions). In our case we’ll just call it my-view.js

export class MyView {
    people = [
        {name: 'Dwayne', location: 'Australia'},
        {name: 'Rob', location: 'USA'},
        {name: 'Max', location: 'France'}
    ];
}
Create a View (aka controller template)

Conclusion

The difference is night and day between the two. As you can see porting over Angular directives to Aurelia is quite easy and considerably less code. I am sure there are ways to make my Angular examples smaller, but the way that I wrote them is probably how 98% of all Angular developers would create them.

In another post and conclusive part II, we will be looking at moving over Angular directives being used as custom attributes.