Thursday, 7 June 2018

Angular - Attribute - 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.
Begin by adding Input to the list of symbols imported from @angular/core.src/app/highlight.directive.ts (imports)
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
Add a highlightColor property to the directive class like this:
src/app/highlight.directive.ts (highlightColor)
@Input() highlightColor: string;


Binding to an @Input property
Notice the @Input decorator. 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.
Try it by adding the following directive binding variations to the AppComponent template:
src/app/app.component.html (excerpt)
<p appHighlight highlightColor="yellow">Highlighted in yellow</p>
<p appHighlight [highlightColor]="'orange'">Highlighted in orange</p>
Add a color property to the AppComponent.
src/app/app.component.ts (class)
export class AppComponent {
  color = 'yellow';
}
Let it control the highlight color with a property binding.
src/app/app.component.html (excerpt)
<p appHighlight [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.
src/app/app.component.html (color)
<p [appHighlight]="color">Highlight me!</p>
The [appHighlight] 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 ([appHighlight]) to do both jobs. That's a crisp, compact syntax.
You'll have to rename the directive's highlightColor property to appHighlight because that's now the color property binding name.
src/app/highlight.directive.ts (renamed to match directive selector)
@Input() appHighlight: string;
This is disagreeable. The word, appHighlight, 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.
src/app/highlight.directive.ts (color property with alias)
@Input('appHighlight') highlightColor: string;
Inside the directive the property is known as highlightColorOutside the directive, where you bind to it, it's known as appHighlight.
You get the best of both worlds: the property name you want and the binding syntax you want:
src/app/app.component.html (color)
<p [appHighlight]="color">Highlight me!</p>
Now that you're binding via the alias to the highlightColor, modify the onMouseEnter() method to use that property. If someone neglects to bind to appHighlightColor, highlight the host element in red:
src/app/highlight.directive.ts (mouse enter)
@HostListener('mouseenter') onMouseEnter() {
  this.highlight(this.highlightColor || 'red');
}
Here's the latest version of the directive class.
src/app/highlight.directive.ts (excerpt)
import { Directive, ElementRef, HostListener, Input } from '@angular/core';

  selector: '[appHighlight]'
})
export class HighlightDirective {

  constructor(private el: ElementRef) { }

  @Input('appHighlight') highlightColor: string;

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor || 'red');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string) {
    this.el.nativeElement.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:
src/app/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 [appHighlight]="color">Highlight me!</p>
Revise the AppComponent.color so that it has no initial value.
src/app/app.component.ts (class)
export class AppComponent {
  color: string;
}
Here are the harness and directive in action.














No comments:

Post a Comment