Книга: Angular 2 Cookbook
Назад: Generating and capturing custom events using EventEmitter
Дальше: Projecting nested content using ngContent

Attaching behavior to DOM elements with directives

In the course of creating applications, you will often find it useful to be able to attach component-style behavior to DOM elements, but without the need to have templating. If you were to attempt to construct an Angular 2 component without providing a template in some way, you will meet with a stern error telling you that some form of template is required.

Here lies the difference between Angular 2 components and directives: components have views (which can take the form of a template, templateUrl, or @View decorator), whereas directives do not. They otherwise behave identically and provide you with the same behavior.

Note

The code, links, and a live example of this are available at .

Getting ready

Suppose you have the following application:

[app/article.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'article',     template: `<h1>{{title}}</h1>`,     styles: [`       h1 {         text-overflow: ellipsis;         white-space: nowrap;         overflow: hidden;         max-width: 300px;       }     `]   })   export class ArticleComponent {      title:string = `Presidential Candidates Respond to        Allegations Involving Ability to Dunk`;   }   

Currently, this application is using CSS to truncate the article title with an ellipsis. You would like to expand this application so that the Article component reveals the entire title when clicked by simply adding an HTML attribute.

How to do it...

Begin by defining the basic class that will power the attribute directive and add it to the application module:

[app/click-to-reveal.directive.ts]      export class ClickToRevealDirective {     reveal(target) {       target.style['white-space'] = 'normal';      }   }     [app/app.module.ts]      import {NgModule} from '@angular/core';   import {BrowserModule} from '@angular/platform-browser';   import {ArticleComponent} from './article.component';   import {ClickToRevealDirective}      from './click-to-reveal.directive';      @NgModule({     imports: [       BrowserModule     ],     declarations: [       ArticleComponent,       ClickToRevealDirective     ],     bootstrap: [       ArticleComponent     ]   })   export class AppModule {}   

First, you must decorate the ClickToRevealDirective class as @Directive and use it inside the Article component:

[app/click-to-reveal.directive.ts]      import { Directive} from '@angular/core';      @Directive({     selector: '[click-to-reveal]'   })   export class ClickToRevealDirective {     reveal(target) {       target.style['white-space'] = 'normal';      }   }   

Next, add the attribute to the element that you wish to apply the directive to:

[app/article.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'article',     template: `<h1 click-to-reveal>{{ title }}</h1>`,     styles: [`       h1 {         text-overflow: ellipsis;         white-space: nowrap;         overflow: hidden;         max-width: 300px;       }     `]   })   export class ArticleComponent {      title: string;     constructor() {        this.title = `Presidential Candidates Respond to          Allegations Involving Ability to Dunk`;     }   }   

Note

Note that the Directive is using an attribute CSS selector to associate itself with any elements that have click-to-reveal. This of course approximates an Angular 1 attribute's directive behavior, but this form is far more flexible since it can wield the innate matchability of selectors.

Now that the Article component is aware of ClickToRevealDirective, you must provide it the ability to attach itself to click events.

Attaching to events with HostListeners

An attentive developer will have noticed that up until this point in the chapter, you have created components that listen to the events generated by the children. This is no problem since you can expressively set listeners in a parent component template on the child tag.

However, in this situation, you are looking to add a listener to the same element that the directive is being attached to. What's more, you do not have a good way of adding an event binding expression to the template from inside a directive. Ideally, you would like to not have to expose this method from inside the directive. How should you proceed then?

The solution is to utilize a new Angular construct called HostListener. Simply put, it allows you to capture self-originating events and handle them internally:

[app/click-to-reveal.directive.ts]      import { Directive, HostListener} from '@angular/core';      @Directive({     selector: '[click-to-reveal]'   })   export class ClickToRevealDirective {     @HostListener('click', ['$event.target'])     reveal(target) {       target.style['white-space'] = 'normal';      }   }      

With this, click events on the <h1> element should successfully invoke the reveal() method.

How it works...

The directive needs a way to attach to native click events. Furthermore, it needs a way to capture objects such as $event that Angular provides to you; these objects would normally be captured in the binding expression.

@HostListener decorates a directive method to act as the designated event handler. The first argument in its invocation is the event identification string (here, click, but it could just as easily be a custom event from EventEmitter), and the second argument is an array of string arguments that are evaluated as expressions.

There's more...

You are not restricted to one HostListener inside a directive. Using it merely associates an event with a directive method. So you are able to stack multiple HostListener declarations on a single handler, for example, to listen for both a click and mouseover event.

See also

  • Using decorators to build and style a simple component describes the building blocks of implementing an Angular 2 component
  • Passing members from a parent component into a child component goes through the basics of downward data flow between components
  • Using ngFor and ngIf structural directives for model-based DOM control instructs you in how to utilize some of Angular 2's core built-in directives
  • Attribute property binding shows Angular 2's clever way of deep referencing element properties
Назад: Generating and capturing custom events using EventEmitter
Дальше: Projecting nested content using ngContent

thank you
Flame
cant read the code since it is all on a single line. Also this comments section is russian
Rakuneque
DATA COLLECTION AND ANALYSIS Two reviewers extracted data and assessed methodological quality independently lasix torsemide conversion Many others were in that space already