two kinds of attribute directives
-
Attribute Directives
look like a normal HTML attribute (possibly with databinding or event binding)
only affect/change the lement they are added to
-
Structrural Directives
look like a normal HTML attribute but a leading * (for de-sugaring)
affect a whole area in the DOM (elements get added/removed)
Top
Index
creating a basic attribute directive
a simple directive
import { Directive, ElementRef, OnInit } from '@angular/core';
@Directive({
selector: '[appBasicHighlight]'
})
export class BasicHighlightDirective implements OnInit {
// c'tor arg is the element the directive is attached to
constructor(private element: ElementRef) {}
ngOnInit() {
this.element.nativeElement.style.backgroundColor = 'green';
}
}
register directive in AppModule
...
import { BasicHighlightDirective } from './basic-highlight/basic-highlight.directive';
@NgModule({
declarations: [
AppComponent,
BasicHighlightDirective
],
...
})
export class AppModule { }
directive attached to an element
<p appBasicHighlight>
This text is highlighted by a directive
</p>
Top
Index
using the renderer to build a better attribute directive
directive above accesses the element directly which is not best practice
better approach to accessing element
service workers may not have access to the DOM
using Renderer to access DOM is safer
below a renderer in injected and ngOnInit uses renderer to set the style
import { Directive, ElementRef, OnInit, Renderer2 } from '@angular/core';
@Directive({
selector: '[appBetterHighlight]'
})
export class BetterHighlightDirective implements OnInit {
constructor(private element: ElementRef, private renderer: Renderer2) { }
ngOnInit() {
this.renderer.setStyle(this.element.nativeElement, 'backgroundColor', 'blue');
}
}
register and use as shown in previous note
Top
Index
using HostListener to listen to host events
directive changes background color when cursor hovers
import HostListener
use directive to listen for events
background color changes when mose enters or leaves control
import { Directive, ElementRef, HostListener, OnInit, Renderer2 } from '@angular/core';
@Directive({
selector: '[appBetterHighlight]'
})
export class BetterHighlightDirective implements OnInit {
constructor(private element: ElementRef, private renderer: Renderer2) { }
ngOnInit() {}
@HostListener('mouseenter') mouseenter(event: Event) {
this.renderer.setStyle(this.element.nativeElement, 'backgroundColor', 'blue');
}
@HostListener('mouseleave') mouseleave(event: Event) {
this.renderer.setStyle(this.element.nativeElement, 'backgroundColor', 'transparent');
}
}
Top
Index
using HostBinding to bind to host properties
use HostBinding directive along with HostListener directives
import { Directive,HostBinding, HostListener } from '@angular/core';
@Directive({
selector: '[appBetterHighlight]'
})
export class BetterHighlightDirective {
@HostBinding('style.backgroundColor') backgroundColor = 'transparent';
@HostListener('mouseenter') mouseenter(event: Event) {
this.backgroundColor = 'blue';
}
@HostListener('mouseleave') mouseleave(event: Event) {
this.backgroundColor = 'transparent';
}
}
Top
Index
binding to directive properties
add a pair of properties decorated with Input directives
set the backgroundColor assignments using the new properties
OnInit is used to initialize the backgroundColor property because the component
has been created but not yet rendered
import { Directive, HostBinding, HostListener, Input, OnInit } from '@angular/core';
@Directive({
selector: '[appBetterHighlight]'
})
export class BetterHighlightDirective implements OnInit {
@Input() defaultColor = 'transparent';
@Input() highlightColor = 'yellow';
@HostBinding('style.backgroundColor') backgroundColor: string;
ngOnInit() {
this.backgroundColor = this.defaultColor;
}
@HostListener('mouseenter') mouseenter(event: Event) {
this.backgroundColor = this.highlightColor;
}
@HostListener('mouseleave') mouseleave(event: Event) {
this.backgroundColor = this.defaultColor;
}
}
used in markup
<p appBetterHighlight [defaultColor]="'transparent'" [highlightColor]="'lightblue'">
This text is highlighted by a better directive
</p>
Top
Index
what happens behind the scenes on structural directives
* indicates a structural directive
<!-- cleaner way to do ... -->
<ul class="list-group">
<div *ngIf='onlyOdd'>
<li class="list-group-item" *ngFor='let number of odd'>
{{ number}}
</li>
</div>
<!-- what is done under the hood -->
<ng-template [ngIf]="onlyOdd">
<div>
<li class="list-group-item" *ngFor='let number of odd'>
{{ number}}
</li>
</div>
</ng-template>
</ul>
Top
Index
building a structural directive
directive is opposite of ngIf
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appUnless]'
})
export class UnlessDirective {
// property must have same name as selector
@Input() set appUnless(condition: boolean) {
if (!condition) {
this.viewContinerRef.createEmbeddedView(this.templateRef);
} else {
this.viewContinerRef.clear();
}
}
constructor(private templateRef: TemplateRef<any>, private viewContinerRef: ViewContainerRef) { }
}
component's markup using new directives
<ul class="list-group">
<!-- cleaner way to do ... -->
<div *ngIf='onlyOdd'>
<li class="list-group-item" *ngFor='let number of odd'>
{{ number}}
</li>
</div>
<!-- what is done under the hood -->
<ng-template [ngIf]="onlyOdd">
<div>
<li class="list-group-item" *ngFor='let number of odd'>
{{ number}}
</li>
</div>
</ng-template>
<!-- using structural directive ... -->
<div *appUnless="onlyOdd" >
<li class="list-group-item" *ngFor='let number of odd'>
{{ number}}
</li>
</div>
</ul>
Top
Index
value is a property of the markup's component
<div [ngSwitch]="value">
<p *ngSwitchCase="5">The value is 5</p>
<p *ngSwitchCase="10">The value is 10</p>
<p *ngSwitchCase="15">The value is 15</p>
<p *ngSwitchDefault>The value is default</p>
</div>
Top
Index
building and using a dropdown directive
import { Directive, HostBinding, HostListener } from '@angular/core';
@Directive({
selector: '[appDropdown]'
})
export class DropdownDirective {
@HostBinding('class.open') isOpen = false;
@HostListener('click') toggleOpen(){
this.isOpen = !this.isOpen;
}
}
<!-- the directive is used here
when the element is clicked the HostListener handles the event an resets the boolean
when the boolean is changes the HostBinding sets the element's open property to the new value
-->
<div class='btn-group' style='padding-top: 25px;' appDropdown>
<button type='button' class='btn btn-primary dropdown-toggle'>Manage Recipe <span class='caret'></span></button>
<ul class='dropdown-menu'>
<li><a href='#'>To Shopping List</a></li>
<li><a href='#'>Edit Recipe</a></li>
<li><a href='#'>Delete Recipe</a></li>
</ul>
</div>
Top
Index