Search

#### Docs

- Guide
  - Overview
  - Setup
  - Learning Angular
  - Architecture
  - Displaying Data
  - User Input
  - Forms
  - Dependency Injection
  - Template Syntax
- Tutorial
  - Introduction
  - 0. The Starter App
  - 1. The Hero Editor
  - 2. Master/Detail
  - 3. Multiple Components
  - 4. Services
  - 5. Routing
  - 6. HTTP
- Advanced
  - Attribute Directives
  - Component Styles
  - Deployment
  - Hierarchical Injectors
  - HTTP Client
  - Lifecycle Hooks
  - Pipes
  - Router
  - Security
  - Structural Directives
  - Testing

#### Components

- Components Gallery

#### Resources

- Cheat Sheet
- Glossary
- FAQ
- Tools
- Community and Support

#### Reference

- Videos
- API Documentation

new_releases We're currently in the progress of migrating this site from Angular version 5.3.1 to 7.0.0. Package versions might be out-of-date or inconsistent! Visit the repo for more info.

# Attribute Directives 6.0

<?code-excerpt path-base=”examples/ng/doc/attribute-directives”?>

An attribute directive changes the appearance or behavior of a DOM element.

Try the live example (view source).

## Directives overview

There are three kinds of directives in Angular:

1. Components—directives with a template.
2. Structural directives—change the DOM layout by adding and removing DOM elements.
3. Attribute directives—change the appearance or behavior of an element, component, or another directive.

Components are the most common of the three directives. You saw a simple component in the Starter App.

Structural Directives change the structure of the view. Two examples are NgFor and NgIf. Learn about them in the Structural Directives page.

Attribute directives are used as attributes of elements. The built-in NgStyle directive in the Template Syntax page, for example, can change several element styles at the same time.

There are two kinds of attribute directive:

- Class-based: A full-featured attribute directive, implemented using a class.
- Functional: A stateless attribute directive, implemented using a top-level function.

## Create a class-based attribute directive

Creating a class-based attribute directive requires writing a controller class annotated with @Directive(), which specifies the selector that identifies the attribute. The controller class implements the desired directive behavior.

This page demonstrates building a simple myHighlight attribute directive to set an element’s background color when the user hovers over that element. You can apply it like this:

<?code-excerpt “lib/app_component_1.html (applied)”?>

      <p myHighlight>Highlight me!</p>

### Write the directive code

Follow the setup instructions for creating a new local project named `attribute_directives`.

Create the following source file in the indicated folder:

<?code-excerpt “lib/src/highlight_directive_1.dart” title?>

      import 'dart:html';

      import 'package:angular/angular.dart';

      @Directive(selector: '[myHighlight]')
      class HighlightDirective {
        HighlightDirective(Element el) {
          el.style.backgroundColor = 'yellow';
        }
      }

`@Directive()` requires a CSS selector to identify the HTML in the template that is associated with the directive. The CSS selector for an attribute is the attribute name in square brackets. Here, the directive’s selector is `[myHighlight]`. Angular locates all elements in the template that have an attribute named `myHighlight`.

### Why not call it “highlight”?

Though highlight is a more concise name than myHighlight and would work, a best practice is to prefix selector names to ensure they don’t conflict with standard HTML attributes. This also reduces the risk of colliding with third-party directive names.

Make sure you do not prefix the `highlight` directive name with `ng` because that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose. For a simple demo, the short prefix, `my`, helps distinguish your custom directive.

After the `@Directive()` metadata comes the directive’s controller class, called `HighlightDirective`, which contains the logic for the directive.

Angular creates a new instance of the directive’s controller class for each matching element, injecting an HTML Element into the constructor.

## Apply the attribute directive

To use the new `HighlightDirective`, create a template that applies the directive as an attribute to a paragraph (`<p>`) element. In Angular terms, the `<p>` element is the attribute host.

Put the template in its own `app_component.html` file that looks like this:

<?code-excerpt “lib/app_component_1.html” title?>

      <h1>My First Attribute Directive</h1>
      <p myHighlight>Highlight me!</p>

Now reference this template in the `AppComponent`, and add the `Highlight` directive to the `directives` list. This way Angular recognizes the directive when it encounters `myHighlight` in the template.

<?code-excerpt “lib/app_component.dart” title?>

      import 'package:angular/angular.dart';

      import 'src/auto_id_directive.dart';
      import 'src/highlight_directive.dart';

      @Component(
        selector: 'my-app',
        templateUrl: 'app_component.html',
        directives: [autoIdDirective, HighlightDirective],
      )
      class AppComponent {
        String color;
      }

open_in_browser Refresh the browser. The app runs, and the `myHighlight` directive highlights the paragraph text.

### Your directive isn’t working?

Did you remember to set the `directives` attribute of `@Component`? It is easy to forget! Open the console in the browser tools and look for an error like this:

    EXCEPTION: Template parse errors:
    Can't bind to 'myHighlight' since it isn't a known property of 'p'.

Angular detects that you’re trying to bind to something but it can’t find this directive. You let Angular know by listing `HighlightDirective` in the `directives` list.

To summarize, Angular found the `myHighlight` attribute on the `<p>` element. It created an instance of the `HighlightDirective` class and injected a reference to the `<p>` element into the directive’s constructor which sets the `<p>` element’s background style to yellow.

## Respond to user-initiated events

Currently, `myHighlight` simply sets an element color. The directive could be more dynamic. It could detect when the user mouses into or out of the element and respond by setting or clearing the highlight color.

Add two eventhandlers that respond when the mouse enters or leaves, each adorned by the `HostListener` annotation.

<?code-excerpt “lib/src/highlight_directive_2.dart (mouse-methods)”?>

      @HostListener('mouseenter')
      void onMouseEnter() {
        _highlight('yellow');
      }

      @HostListener('mouseleave')
      void onMouseLeave() {
        _highlight();
      }

      void _highlight([String color]) {
        _el.style.backgroundColor = color;
      }

The `@HostListener` annotation lets you subscribe to events of the DOM element that hosts an attribute directive, the `<p>` in this case.

Of course you could reach into the DOM with standard JavaScript and and attach event listeners manually. There are at least three problems with that approach:

1. You have to write the listeners correctly.
2. The code must detach the listener when the directive is destroyed to avoid memory leaks.
3. Talking to DOM API directly isn’t a best practice.

The handlers delegate to a helper method that sets the color on the DOM element, `_el`, which you declare and initialize in the constructor.

<?code-excerpt “lib/src/highlight_directive_2.dart (constructor)” region=”ctor” title?>

      final Element _el;

      HighlightDirective(this._el);

Here’s the updated directive in full:

<?code-excerpt “lib/src/highlight_directive_2.dart” plaster=”none” title?>

      import 'dart:html';

      import 'package:angular/angular.dart';

      @Directive(selector: '[myHighlight]')
      class HighlightDirective {
        final Element _el;

        HighlightDirective(this._el);

        @HostListener('mouseenter')
        void onMouseEnter() {
          _highlight('yellow');
        }

        @HostListener('mouseleave')
        void onMouseLeave() {
          _highlight();
        }

        void _highlight([String color]) {
          _el.style.backgroundColor = color;
        }
      }

open_in_browser Refresh the browser. Confirm that the background color appears when the mouse hovers over the `p` and disappears as it moves out.

## Pass values into the directive with an @Input data binding

Currently the highlight color is hard-coded within the directive. That’s inflexible. In this section, you give the developer the power to set the highlight color while applying the directive.

Start by adding a `highlightColor` property to the directive class like this:

<?code-excerpt “lib/src/highlight_directive_2.dart (highlightColor)” region=”color” title?>

      @Input()
      String highlightColor;

### Binding to an @Input property

Notice the `@Input` annotation. It adds metadata to the class that makes the directive’s `highlightColor` property available for binding.

It’s called an input property because data flows from the binding expression into the directive. Without that input metadata, Angular rejects the binding; see below for more about that.

Try it by adding the following directive binding variations to the `AppComponent` template:

<?code-excerpt “lib/app_component_1.html (excerpt)” region=”color-1” title?>

      <p myHighlight highlightColor="yellow">Highlighted in yellow</p>
      <p myHighlight [highlightColor]="'orange'">Highlighted in orange</p>

Add a `color` property to the `AppComponent`.

<?code-excerpt “lib/app_component_1.dart (class)” title?>

      class AppComponent {
        String color = 'yellow';
      }

Let it control the highlight color with a property binding.

<?code-excerpt “lib/app_component_1.html (excerpt)” region=”color-2” title?>

      <p myHighlight [highlightColor]="color">Highlighted with parent component's color</p>

That’s good, but it would be nice to simultaneously apply the directive and set the color in the same attribute like this.

<?code-excerpt “lib/app_component.html (color)”?>

      <p [myHighlight]="color">Highlight me!</p>

The `[myHighlight]` attribute binding both applies the highlighting directive to the `<p>` element and sets the directive’s highlight color with a property binding. You’re re-using the directive’s attribute selector (`[myHighlight]`) to do both jobs. That’s a crisp, compact syntax.

You’ll have to rename the directive’s `highlightColor` property to `myHighlight` because that’s now the color property binding name.

<?code-excerpt “lib/src/highlight_directive_2.dart (renamed to match directive selector)” region=”color-2” title?>

      @Input()
      String myHighlight;

This is disagreeable. The word, `myHighlight`, is a terrible property name and it doesn’t convey the property’s intent.

### Bind to an @Input alias

Fortunately you can name the directive property whatever you want and alias it for binding purposes.

Restore the original property name and specify the selector as the alias in the argument to `@Input`.

<?code-excerpt “lib/src/highlight_directive.dart (color property with alias)” region=”color” title?>

      @Input('myHighlight')
      String highlightColor;

Inside the directive the property is known as `highlightColor`. Outside the directive, where you bind to it, it’s known as `myHighlight`.

You get the best of both worlds: the property name you want and the binding syntax you want:

<?code-excerpt “lib/app_component.html (color)”?>

      <p [myHighlight]="color">Highlight me!</p>

Now that you’re binding to `highlightColor`, modify the `onMouseEnter()` method to use it. If someone neglects to bind to `highlightColor`, highlight in red:

<?code-excerpt “lib/src/highlight_directive_3.dart (mouse enter)” region=”mouse-enter” title?>

      @HostListener('mouseenter')
      void onMouseEnter() => _highlight(highlightColor ?? 'red');

Here’s the latest version of the directive class.

<?code-excerpt “lib/src/highlight_directive_3.dart” title?>

      import 'dart:html';

      import 'package:angular/angular.dart';

      @Directive(selector: '[myHighlight]')
      class HighlightDirective {
        final Element _el;

        HighlightDirective(this._el);

        @Input('myHighlight')
        String highlightColor;

        @HostListener('mouseenter')
        void onMouseEnter() => _highlight(highlightColor ?? 'red');

        @HostListener('mouseleave')
        void onMouseLeave() => _highlight();

        void _highlight([String color]) {
          _el.style.backgroundColor = color;
        }
      }

## Write a harness to try it

It may be difficult to imagine how this directive actually works. In this section, you’ll turn `AppComponent` into a harness that lets you pick the highlight color with a radio button and bind your color choice to the directive.

Update `app_component.html` as follows:

<?code-excerpt “lib/app_component.html (v2)”?>

      <h1>My First Attribute Directive</h1>

      <h4>Pick a highlight color</h4>
      <div>
        <input type="radio" name="colors" (click)="color='lightgreen'">Green
        <input type="radio" name="colors" (click)="color='yellow'">Yellow
        <input type="radio" name="colors" (click)="color='cyan'">Cyan
      </div>
      <p [myHighlight]="color">Highlight me!</p>

Revise the `AppComponent.color` so that it has no initial value.

<?code-excerpt “lib/app_component.dart (class)”?>

      class AppComponent {
        String color;
      }

open_in_browser Refresh the browser. Here are the harness and directive in action.

## Bind to a second property

This highlight directive has a single customizable property. In a real app, it may need more.

At the moment, the default color—the color that prevails until the user picks a highlight color—is hard-coded as “red”. Let the template developer set the default color.

Add a second input property to `HighlightDirective` called `defaultColor`:

<?code-excerpt “lib/src/highlight_directive.dart (defaultColor)” title?>

      @Input()
      String defaultColor;

Revise the directive’s `onMouseEnter` so that it first tries to highlight with the `highlightColor`, then with the `defaultColor`, and falls back to “red” if both properties are undefined.

<?code-excerpt “lib/src/highlight_directive.dart (mouse-enter)”?>

      @HostListener('mouseenter')
      void onMouseEnter() => _highlight(highlightColor ?? defaultColor ?? 'red');

How do you bind to a second property when you’re already binding to the `myHighlight` attribute name?

As with components, you can add as many directive property bindings as you need by stringing them along in the template. The developer should be able to write the following template HTML to both bind to the `AppComponent.color` and fall back to “violet” as the default color.

<?code-excerpt “lib/app_component.html (defaultColor)”?>

      <p [myHighlight]="color" defaultColor="violet">
        Highlight me too!
      </p>

Angular knows that the `defaultColor` binding belongs to the `HighlightDirective` because you made it public with the `@Input` annotation.

open_in_browser Refresh the browser. Here’s how the harness should work when you’re done coding.

## Write a functional directive

A functional directive is a stateless directive that is rendered once. You create a functional directive by annotating a public, top-level function with @Directive().

Create the following functional attribute directive:

<?code-excerpt “lib/src/auto_id_directive.dart” title?>

      import 'dart:html';
      import 'package:angular/angular.dart';

      int _idCounter = 0;

      @Directive(selector: '[autoId]')
      void autoIdDirective(
        Element el,
        @Attribute('autoId') String prefix,
      ) {
        el.id = '$prefix${_idCounter++}';
      }

Like constructor parameters in class-based directives, function parameters define the functional directive’s dependencies.

When you write a functional directive, follow these rules:

- Make the function return type `void`.
- In the @Directive() annotation, use only the `selector` parameter and, if necessary, `providers`.

While functional directives are stateless, they can be impure (making use of global state), as the `autoId` directive shows.

Add the following lines at the end of the app component template: <?code-excerpt “lib/app_component.html (autoId)” title?>

      <h4 #h1 autoId="heading-">Auto-ID at work</h4>
      <p>The previous heading has ID {{h1.id}}</p>

      <h4 #h2 autoId="heading-">Auto-ID at work, again</h4>
      <p>The previous heading has ID {{h2.id}}</p>

open_in_browser Refresh the browser. The app reports headings ID `heading-0` and `heading-1`.

## Summary

This page covered how to:

- Create a class-based attribute directive that modifies the behavior of an element.
- Apply an attribute directive to an element in a template.
- Respond to events that change a class-based directive’s behavior.
- Bind values to a class-based directive.
- Write a functional attribute directive.

The final source code follows:

You can also experience and download the live example (view source).

### Appendix: Why add @Input?

In this demo, the `hightlightColor` property is an input property of the `HighlightDirective`. You’ve seen it applied without an alias:

<?code-excerpt “lib/src/highlight_directive_2.dart (color)”?>

      @Input()
      String highlightColor;

You’ve seen it with an alias:

<?code-excerpt “lib/src/highlight_directive.dart (color)”?>

      @Input('myHighlight')
      String highlightColor;

Either way, the `@Input` annotation tells Angular that this property is public and available for binding by a parent component. Without `@Input`, Angular refuses to bind to the property.

You’ve bound template HTML to component properties before and never used `@Input`. What’s different?

The difference is a matter of trust. Angular treats a component’s template as belonging to the component. The component and its template trust each other implicitly. Therefore, the component’s own template may bind to any property of that component, with or without the `@Input` annotation.

But a component or directive shouldn’t blindly trust other components and directives. The properties of a component or directive are hidden from binding by default. They are private from an Angular binding perspective. When adorned with the `@Input` annotation, the property becomes public from an Angular binding perspective. Only then can it be bound by some other component or directive.

You can tell if `@Input` is needed by the position of the property name in a binding.

- When it appears in the template expression to the right of the equals (=), it belongs to the template’s component and does not require the `@Input` annotation.

- When it appears in square brackets ([ ]) to the left of the equals (=), the property belongs to some other component or directive; that property must be adorned with the `@Input` annotation.

Now apply that reasoning to the following example:

<?code-excerpt “lib/app_component.html (color)”?>

      <p [myHighlight]="color">Highlight me!</p>

- The `color` property in the expression on the right belongs to the template’s component. The template and its component trust each other. The `color` property doesn’t require the `@Input` annotation.

- The `myHighlight` property on the left refers to an aliased property of the `HighlightDirective`, not a property of the template’s component. There are trust issues. Therefore, the directive property must carry the `@Input` annotation.