Structural directives in Angular

Vojtech Mašek
ITNEXT
Published in
4 min readAug 7, 2018

--

Are responsible for HTML layout. They shape or reshape the DOM’s structure, typically by adding, removing, or manipulating elements.

Structural directives are a key part of Angular everyone should be familiar with. They are responsible for manipulating DOM through adding, removing or changing the elements. Even if you have never written a structural directive yourself, you have probably been using *ngIf and *ngFor in your templates pretty often. The asterisk ( * ) states it is a structural directive. If you want to read more before proceeding to our examples, angular docs provide a nice, detailed explanation.

Writing a structural directive is really easy, as we will see in the examples below. Its purpose can vary from simple conditional rendering (e.g. based on user role) to iteration over a range or in-template let-variable declaration.

Basics

Manipulating the DOM

For a directive to be able to manipulate the DOM, it is necessary to provide a way to do it without actually changing something via DOM API. In Angular, it is done by injecting a view container reference and a template reference to the directive.

Example of dummy directive with view container and template references

View Container reference

Represents a container where one or more Views can be attached.

It basically allows us to manipulate the container (outer element wrapper) and attach templates created inside of the directive. More is described in its docs.

Template reference

Represents an Embedded Template that can be used to instantiate Embedded Views.

In plain English, it is an element that we’ve put the directive on. We need this reference in the directive because we almost always use the template in some way, either by repeating it (*ngFor), conditionally displaying it (*ngIf) or attaching another element to it on hover (matTooltip).

Value input

In most cases, we input some value into the directive so it can be used internally. For example, a collection to iterate over, a condition to evaluate or a value to be used for a more specific purpose. The digest cycle of the directive is tied also to the value change, so each time it changes, the directive is reevaluated.

It isn’t always necessary to input the values; services or state store can be injected and the directive can access the values the same way a component would.

Context

Every directive has its own context. It is a good practice to define the context as an interface for every directive, so it is understandable what values we expose. This context is then used as a type for the template reference and the properties are set when creating an embedded view on the container reference.

Exposing the internal values

There are two possible ways to expose the internal values so we can use them in a template: one is naming the variables in the structure and the other is emitting an implicit value.

For example, the *ngFor directive iterates through the list (array), set properties in its context object and exposes the current item. Exposed values can be used in the template inside the directive-decorated element’s scope.

In *ngFor there are currently index, even, odd, first, last context variables exported via their names and one implicit variable representing the current item that we can name as we desire to. In the example below, it’s hero. It should be assigned to contexts $implicit property.

Usage of the variables exposed from *ngFor directive

Let directive

If you’ve adopted the usage of async pipe in your templates, you have probably stumbled upon a use case where you would write something like this:

Using the async pipe to get the current value of the user$ data stream and assigning it to user variable

It allows you to reuse/share the value in the variable you’ve created with “as” syntax. It is really useful if you don’t want to use async pipe multiple times because it creates a subscription on each usage.

The *ngIf directive serves two purposes there. One is letting us create the variable for async value and the other is hiding/not rendering this whole section if the value is falsy (false, undefined, null, 0,…).

But what if we want the value even if it is “falsy”? We can take advantage of let directive.

Example of simple let directive that allows in template variables

Let directive’s context has two properties (as described in its docs). Both of those properties are then used with different syntax they enable for template variable declaration in the usage example’s HTML template.

Some might say that we should handle these cases with else statement within the *ngIf and yes, it is a perfectly valid solution for most of the cases. The good use-case is handling the falsy values with some sort of custom messages. But not always you want or can separate logic for falsy and truthy values in template, so using a directive like this could make the template a lot simpler and readable.

For each of range directive

Iterating in templates sometimes requires various things. One of them is having an option to easily display some given number of elements. A good example is the drop-down of numeric values either for table pagination or year select, which can be easily done using it like in the example below.

Code examples repository

All examples, usages and source code is at the link below.

My talk on Structural Directives at Angular Vienna meet-up

--

--

Head of Engineering @ FlowUp.cz ⬆️ Angular & TypeScript enthusiast, Speaker 🔊, Stay at home astronaut 🌍