Re-usable Angular components — Part II

In this part we take a look at structural directives and how we can use them to build even more generic components. In the first part we discussed other approaches to build a re-usable component.

What is a structural directive?

A structural directive is defined like any other directive but with one major difference: In the constructor and/or is injected.

allows to access the projected DOM content of the element where the structural directive is applied on, whereas is the service that allows to render any given .

As soon as and/or is injected into the constructor the directive becomes a structural directive.
This also changes how the directive is applied on an element: A structural directive can be applied only on elements or the asterix notation has to be used.

Asterix Notation - *directiveName

You might not have used in your application but you definitely have used structural directives. This is because structural directives usually are used in a different way. As syntactic sugar angular allows to use a structural directive with an asterix prefix. So instead of writing the shorter asterix form can be used 
Under the hood angular translates it back into the form.

Rendering

The template of a structural can be rendered by using the which offers the method to render the template called . The template can also be rendered in the template of a component by using the structural directive .

expects a as input parameter which it will render.
In a nutshell this is how works:

Of course if you check the source code it is a bit more complicated than that, but the core functionality is just rendering the and use the element where it is applied on as container.

Input and Output

Another major difference to a directive is that structural directives can not have any .

Also parameters works differently as for normal directives. All have to be prefixed with the directive selector name.

Also can not be as attributes but by using an expression. In the example above we have specified two .
However, will not work. Instead, we have to use the expression grammar.

The expression language of structural directives is quite powerful and allows you to do complex stuff.

Expression grammar

The expression grammar of a structural direction not only allows you to set parameters of your directive but also to receive input, called context when it is rendered.

Angular specifies the grammar like

Thus, the following inputs would be valid.

1: *appOption="'My Text'"

This is passing a string into our . It does not have to be necessarily be a string it can also be any other type. for example accepts a boolean as expression input. In order to work with the expression in the directive it has to be declared as input. However, all inputs of a structural directive have to be prefixed with the directive selector name.

2: *appOption="let i = index"

As mentioned earlier the expression language not only allows to set input on the structural directive but also to receive content back from it. The content received back is called
The context can be either passed as a second parameter when using the or passed as context input when using 
Both allow to pass an optional context object which exposes variables that can be used in the projected content. In this example the structural directive passes the context On the element the context is received and mapped onto the variable i .

3: *appOption="let xyz, let i = index"

Angular also allows to receive any content and to map onto a custom variable name, in this case . This is done with the convention. Content that is set in the context using the name is mapped on all variables, that are not specified in the context, on the receiving side.

3b: *appOption="let xyz, index as i"

It is basically the same as above only that the as notation is used.  is assigned to and can be used in the template as . One has to be careful because this does not work standalone. would result in an error.

4: *appOption="'My text'; color: 'red'; let xyz; let i = index"

In the first example a custom text is passed as input into the directive. This example is extended with a second input which is specified with a key expression. In order to access the color input, it also has to be prefixed with the directive selector name. So becomes available as .

5: *appOption="let xyz setItFrom [1, 2, 3]; let i = index"

This is basically how works in a nutshell. becomes available by prefixing it with the directive selector name. We iterate over all options and render the projected content that is available via the injected . The can be seen as a blueprint from which a new instance with its own context is generated in each call.

Using the structural directive for our appOption

As we now have a basic understanding of how structural directives, we can use that knowledge to apply it on our original problem.
A structural directive allows us to access the projected DOM content of the element where it is applied on. We can use this to register the at our parent .

In our parent component we have to adjust our method to accept a template and take care of the rendering of the registered option template. In the template we iterate over the registered and render each one of them by using .

Now, we have all the functionality and flexibility we were looking for:

Extend it with everything we learned

We can extend the with some more inputs in order to apply everything we learned.

Let's assume we got the following requirements from product management:

  • Some options shall have a color
  • Each entry shall be prefixed with an incremented number
  • We get a list of actions from our backend like .

Our template how we use our structural directive shall look like this

You can try to implement the requirements by yourself as a small exercise, or you go straight to the solution.

Solution

I'm a PWA developer with 7+ years of Angular experience. During the day I develop the WebApp for an IOT business application, during the night I work on aux.app