More Dynamic Directives in AngularJS

AngularJS is all about enabling dynamic, modular apps, and custom directives are a big part of that. We can take things a step further by adding and removing directives dynamically at runtime. Before we do that, though, let’s get a little background.

Interesting things are happening in the world of Angular—shifting technology, shifting names, and even shifting version numbers. With the rise of Angular 2, the impending release of Angular 4 (yes, that was a “4”), and a shift toward referring to all versions of Angular as simply “Angular” (sans “JS” or any version number except when necessary to differentiate between versions) it might seem like good ol’ AngularJS is on the outs. Not yet, it’s not.

AngularJS and Angular 2 can be thought of as two siblings in a family of popular front-end frameworks. They are related, but they are different, and they are both out there doing similar jobs at the same time. Angular 4 will be a direct successor to Angular 2, but AngularJS is still being maintained, it is still very popular, and it will likely remain popular for quite some time.

Whether you call it “AngularJS”, “Angular 1”, or simply “Angular”, we’ll be working with the elder of the two Angular lines in this article. As of this writing the most recent release in this line is 1.6.1, but the techniques we will explore do not depend upon any recent Angular functionality and they have been tested and found to work as far back as version 1.2.32.

Foundational Concepts for Custom Directives

Normally, if you want to use a custom Angular directive you simply add a special element to your HTML then let Angular do its thing when the page loads. For example, let’s say we have a directive named “card”. We might display it like so:

1
<div data-card></div>

That modest HTML element becomes something more when it is processed by Angular: a user interface component that can have additional markup and associated behavior and data. An Angular directive can even contain other directives. For example, our card directive might include a random-greeting directive like so:

1
2
3
// …
template: '<p data-random-greeting></p>',
// …

With all of that in mind, let’s get a little more dynamic.

Adding Custom Directives

First Attempt: Pure JavaScript

What happens when we add our special HTML element after Angular has already processed all of the directives on the page? Let’s try it using pure JavaScript:

1
2
3
4
5
6
7
8
9
10
11
12
13
function addCard(container) {
    var newElement = document.createElement('div');
    container = container || document.body;
    newElement.setAttribute('data-card', '');
    container.appendChild(newElement);
    return newElement;
}
1
<button onclick="addCard()">Add Card</button>

If we load our page, click our “Add Card” button, then look at our page source (or just look at our page if we have specified appropriate styles for our element via CSS) we should see that the expected element has been added to the document, but it hasn’t been processed by Angular. As far as Angular can tell, it’s just a normal HTML element.

“Doesn’t look like anything to me.” – Dolores Abernathy Angular

The Missing Step: Angular Compilation

The key to getting Angular to associate an HTML element with a directive is a quote from Romeo and Juliet element compilation. Instead of using the pure JavaScript above we can do something like the following in our Angular app controller:

1
2
3
4
5
6
7
8
9
$scope.addCard = function(container) {
    var newElement = $compile('<div data-card></div>')($scope);
    container = container || document.body;
    angular.element(container).append(newElement);
};

Don’t forget to update our button to use “ng-click” (“data-ng-click” if we want to keep our HTML5 valid) instead of “onclick”:

1
<button data-ng-click="addCard()">Add Card</button>

That’s it! Now we can add a card whenever we like and Angular will compile it just like a hard-coded directive, including any other directives it contains.

Removing Custom Directives

Now that we can add custom directives on demand we may wish to remove custom directives as well—whether or not we added them at runtime. Since an Angular directive is more than just an HTML element it is not enough for us to remove some HTML from the document; we also need to destroy the associated scope and we may need to perform other clean-up tasks such as removing event listeners.

Angular facilitates directive clean-up tasks via its $destroy() method and an associated event, which can be handled from within in a directive’s link method like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// …
link: function(scope, element, attrs) {
    scope.$on('$destroy', function() {
        console.log('BOOM! Directive scope destroyed!');
        // Remove event listeners and otherwise tidy up here!
        element.remove();
    });
},
// …

Calling $destroy() on a directive is simple. It can even be done from within the directive’s template HTML like so:

1
2
3
// …
template: '<p data-random-greeting></p><button data-ng-click="$destroy()">Destroy me!</button>',
// …

There you have it! You can now add and remove Angular directives at will. Whether you want to add or remove content at runtime these dynamic techniques can be useful, flexible alternatives to directive processing during normal Angular initialization.

Let’s Work Together

From web design, CMS websites, applications, ecommerce, motion graphics and multimedia, graphic design, and print, to Internet marketing solutions, we’ve got you covered.