Creating custom directives in AngularJS

10 Mar 2019, 14:02
by CodeOoze

AngularJS directives are DOM elements with special markers that tell AngularJS to attach certain behaviour to the element. Commonly you’ll see these markers as an element tag (E) or element attribute (A). These special markers are also known as matching types.

Let’s take a look at a couple of examples:

Element (E) matching type:

In this example, an element tag my-directive is used to reference a directive:

<my-directive></mydirective>

Attribute (A) matching type:

In this example, the directive is referenced using an attribute my-directive on a <div>:

<div my-directive="true"></div>

There are 2 additional matching types you may also come across (but are less common): class names (C), and comments (M). I will not be covering these 2 types, so refer to the AngularJS directive guide for more information.

Defining a directive

A common use for directives is to provide a re-usable template, such as rendering a group of buttons. Let’s look at a simple example consisting of a single button with Bootstrap 4 button styling:

<button type="button" class="btn btn-primary">Click Me</button>

In AngularJS, a directive is defined as a function. This function should return an object containing some properties such as template and scope.

The first property we’ll examine is the template property which can contain HTML to be rendered by the directive. Here is a simple directive to render the button:

Before you can start referencing this directive in your AngularJS application, it must be registered.

Registering a directive

Directives are registered against a module using module.directive by passing the normalized (case-sensitive camelCase) directive name and your function. In the previous example of <my-directive> the normalized name would be myDirective.

Let’s take a look with another quick example. Here, our simple directive is registered against the module myApp using it’s normalized name myDirective:

This directive can then be referenced in your HTML using my-directive (see below).

Referencing a directive

Directives in the DOM are referenced in lower-case format and may also use certain delimiters such as "-" to replace camelCase. This is required because HTML is case-insensitive. In our example, myDirective can be referenced as an element tag by using my-directive:

<my-directive></mydirective>

Restrict and matching types

When you define a directive it will by default support both E and A matching types. However, you can also specify which matching types your directive supports by using the restrict property.

Specifying restrict: 'EA' is the same as not specifying any types, since EA is the default value.

<my-directive></mydirective> <!-- works -->
<div my-directive="true"></div> <!-- does not work -->

Similarily, to cause the directive to only support attribute referencing, use restrict: 'A'.

Scope inheritance

The scope of a directive is controlled with the scope property. Unless specified otherwise, the scope property will have a default value of false and share the scope of it’s parent. For example, if the parent is a controller, the directive will have access to the controller’s $scope variable:

In this example, the controller scope includes a variable btnLabel which is accessible by the directive using {{btnLabel}}:

Alternatively, using scope: true will cause the directive to have it’s own scope inherited from the parent. In the above example, the directive would inherit myText with a value of "Hello directive", but modifying the text-box value in the directive will no longer modify the controller’s myText.

The limitation of both scope: false and scope: true is that you can only reference the directive once within a controller. To overcome this, the directive scope must be isolated.

Isolating scope

The scope of a directive may be fully isolated from it’s parent by setting the directive’s scope property to an object.

Isolating the directive scope allows a directive to be referenced multiple times within a controller.

<my-directive></mydirective> <!-- works -->
<my-directive></mydirective> <!-- also works -->

Isolated scope also provides the flexibility to define if individual properties of the parent $scope will be bound to the directive, whether they are simply copied (@) or are directly bound (=).

Let’s take a look at a final example using two $scope variables in the parent controller: myCopiedText and mySharedText. Note that we again use normalized names in the directive. The first variable myCopiedText is copied into the directive’s scope using @ and isolated from the controller scope. The second variable mySharedText is bound to the directive variable of the same name using =, similar to what happened for all variables with scope: false.

Note above we are using the attribute my-copied-text which maps to the normalized directive variable myCopiedText. If the attribute were different, such as something-else we must specify that in the directive scope mapping, again with the normalized equivalent: