Directives Deep Dive
two kinds of attribute directives

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

understanding ngSwitch
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

n4jvp.com