How Directives Work?

Let's learn about the basic syntax and features of directives.

Basic notes and usage

Directives are classes marked by the @Directive decorator, just like components are marked by the @Component decorator.

Here’s an example:

@Directive({
  selector: 'my-directive',
})
export class MyDirective {
...
} 

We must declare directives if a module uses them. So, we need to add them into the declaration field of our NgModule, like this::

@NgModule({
  declarations: [
    MyDirective
  ]
})
export class MyModule {
}

Components Versus Directives

There are lots of similarities between components and directives. The table below lists them:

Components Directives
HTML selector Yes Yes
HTML template Yes No, however, it can modify the existing DOM.
CSS stylesheet Yes No, however, it can change existing styles.
DOM interaction Yes, it’s a custom element Yes, it can be a custom element, or an attribute of an existing element that modifies its behavior or appearance.
Inputs / Outputs Yes, they communicate with the parent component Yes, they communicate with the component at the location that it’s used.

In summary, here’s what we need to remember about the differences between components and directives:

  • A component is a reusable set of DOM elements that creates a UI supplied with custom behavior.
  • A directive is a reusable behavior that can be applied to existing DOM elements.

How do directives work?

There’s no single answer to this question because directives do many things and enhance codebases in many ways. We’ll discuss how later in this course. For now, let’s look at a basic overview.

An essential part of a directive is the constructor, which we can use to inject references that are crucial to our directive.

@Directive({
  selector: '[appBold]'
})
export class BoldDirective {
    constructor(el: ElementRef) {
       el.nativeElement.style.fontWeight = 'bold';
    }
}

In this example, we inject ElementRef, which gives us access to the host DOM element. The host DOM element is the exact element upon which the directive is set. In the constructor code, we can set a bold font-weight style on nativeElement, which is the actual DOM element.

In the directive constructor, we can inject far more than just a reference to the DOM element. Let’s assume we create this directive, declare it in our module, and now want to use it. How do we do that? We can answer that question by examining selectors.

Selector

The Selector property in the @Directive decorator declaration is crucial to understanding how to use the given directive. The selector is simply a CSS selector we use to identify the directive in the template and apply its code to that element.

In CSS, we have a bunch of different selectors. Some of them are very specific while some are more broad.

Here’s a list of the available selectors:

  • The element-name selects by element name.
  • The .class selects by class name.
  • The [attribute] selects by attribute name.
  • The [attribute=value] selects by attribute name and value.
  • The :not(sub_selector) is used only if there’s no match with the sub_selector.
  • The selector1, selector2 is used if the selector1 and selector2 match.

Let’s go back to our example:

@Directive({
  selector: '[appBold]'
})
export class BoldDirective {
    constructor(el: ElementRef) {
       el.nativeElement.style.fontWeight = 'bold';
    }
}

We used the selector attribute. So, what does this tell us? Simply, whenever we specify the appBold custom attribute, we apply BoldDirective. The illustration below presents this concept:

Here is an example:

<p appBold>This text will be bold</p>
<p>This one will be regular</p>

First, we apply the directive to the <p> element, which has our custom attribute. However, the next element doesn’t have that directive, so it remains the same.

Let’s see that in action:

<!--applying appBold directive on the <p> element-->
<p appBold> Header 1</p>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
  aliqua.</p>
A simple directive applied to an element

Selector abilities

The attribute selector is very popular because it can be applied to anything while giving implicit information about where it’s added. The developer needs to add the attribute to each element individually.

Let’s assume we want to add this directive to all the elements with the following CSS class:

class="header"

We would use the selector attribute here and add it manually to every element that has this class. But, what if we add another element? What if there are too many elements to do this manually? In such cases, we would want to learn a process that will add it automatically to every element that meets our requirements, which we’ll learn to do in the next lesson.

Summary

In Angular, directives are similar to components. The main differences are that directives don’t have their own template and are applied to existing DOM elements. Directives are applied to elements by CSS selectors, which are specified in the @Directive declaration.