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.