@tkssharma

github.com/tkssharma

Power of Structural Directives 

 

directive changes the DOM layout by adding and removing DOM elements.

 

 

Create our own version of *NgIf*

 

import { Directive, Input, TemplateRef, ViewContainerRef } 
                  from '@angular/core';

@Directive({ selector: '[myNgIf]'})
export class MyNgIfDirective {

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef) { }

  @Input() set myNgIf(condition: boolean) {
    if (condition) {
      this.viewContainer.createEmbeddedView(this.templateRef);
    } else {
      this.viewContainer.clear();
    }
  }
}
TemplateRef — As the name suggest the TemplateRef is just a reference to the template.

 

 

ViewContainerRef -- container.ViewContainerRef is a reference to the container.

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2 *myNgIf="condition">Hello {{name}}</h2>
      <button (click)="condition = !condition">Click</button>
    </div>
  `,
})
export class App {
  name:string;
  condition = false;
  constructor() {
    this.name = 'Angular2'
  }
}

More Examples .. role based directive

@Directive({selector: '[ifRole]'})
export class IfRoleDirective {
  user$ : Subscription;
  @Input("ifRole") roleName : string;

  constructor( private templateRef : TemplateRef<any>,
               private viewContainer : ViewContainerRef,
               private authService : AuthService ) {
  }

  ngOnInit() {
    this.user$ = this.authService.user
      .do(() => this.viewContainer.clear())
      .filter(user => user.role === this.roleName).subscribe(() => {
        this.viewContainer.createEmbeddedView(this.templateRef);
      });
  }

  ngOnDestroy() {
    this.user$.unsubscribe();
  }
}

This is How you can use it ...

<div *ifRole="'admin'">
  Only for Admin
</div>

<div *ifRole="'client'">
  Only for Client
</div>

<div *ifRole="'editor'">
  Only for Editor
</div>

ViewChildren vs ContentChildren —

 

ContentChildren includes only elements that exists within the ng-content tag.


 

ViewChildren don’t include elements that exist within the ng-content tag.


 

 

 

 

 

ViewChildren vs ContentChildren —

 

 

 

 

 

 

 

ContentChildren — All child tab Instance

 

 

 

 

 

 

 

 

 

@Component({
  selector: 'tab',
  template: `
    <p>{{title}}</p>
  `,
})
export class TabComponent {
  @Input() title;
}

@Component({
  selector: 'tabs',
  template: `
    <ng-content></ng-content>
  `,
})
export class TabsComponent {
 @ContentChildren(TabComponent) tabs: QueryList<TabComponent>
 
 ngAfterContentInit() {
   this.tabs.forEach(tabInstance => console.log(tabInstance))
 }
}

@Component({
  selector: 'my-app',
  template: `
    <tabs>
     <tab title="One"></tab>
     <tab title="Two"></tab>
    </tabs>
  `,
})
export class App {}

ViewChildren decorator will return the component instance, but you can ask for other tokens:

If you want your component to notify his parent about something you can use the Output decorator with EventEmitter to create a custom event.

@Component({
  selector: 'add-todo',
  template: `
   <input type="text" placeholder="Add todo.." [formControl]="control">
   <button (click)="add.next(control.value)">Add</button>
`,
})
export class AddTodoComponent {
  control : FormControl = new FormControl("");
  @Output() add = new EventEmitter();
}








<add-todo (add)="addTodo($event)"></add-todo>

What is EventEmitter .. a subject ??

export declare class EventEmitter<T> extends Subject<T> {
    __isAsync: boolean;
    constructor(isAsync?: boolean);
    emit(value?: T): void;
    subscribe(generatorOrNext?: any, error?: any, complete?: any): any;
}

💪 You have the power of Rx 💪

@Output() add = new EventEmitter().filter(v => !!v);

@Output() add = new BehaviorSubject("Awesome").filter(v => !!v);

EventEmitters are not DOM events

they can't bubble to top of DOM

Event emitter !== DOM

You can't emit event from todo to todoPage component

Solution is

    Keep passing the event up the tree

 

export class TodoComponent {
  @Output() toggle = new EventEmitter<any>();
}

export class TodosComponent {
  @Output() toggle = new EventEmitter<any>();
}

export class TodosPageComponent {
  toggle($event) {}
}

Too much nesting here

Lets do it in native way !

@Component({
  selector: 'app-todo',
  template: `
     <p (click)="toggleTodo(todo)">
      {{todo.title}}
     </p>
   `
})
export class TodoComponent {
  @Input() todo;
  constructor(private el: ElementRef) {}

  toggleTodo(todo) {
    this.el.nativeElement
      .dispatchEvent(new CustomEvent('toggle-todo', {
        detail: todo,
        bubbles: true
      }));
  }
}

Native DOM not a good choice

Lets do using service

@Injectable()
export class TodosService {
  private _toggle = new Subject();
  toggle$ = this._toggle.asObservable();

  toggle(todo) {
    this._toggle.next(todo);
  }
}

export class TodoComponent {
  constructor(private todosService: TodosService) {}
  
  toggle(todo) {
    this.todosService.toggle(todo);
  }
}

export class TodosPageComponent {
  constructor(private todosService: TodosService) {
    todosService.toggle$.subscribe(..);
  }
}

View Encapsulation

import {Component, ViewEncapsulation} 
        from 'angular2/angular2';

@component({
    selector: 'app',
    template: `<p class="green">`,
    style: [` 
            .green { color: green}
        `],
    encapsulation: 'ViewEncapsulation.Native'
                            // .Emulated .None
})

export class App { 

       constructor() {}
}
  1. ViewEncapsulation.Emulated
    • ​angular2 view encapsulation enabled // default
  2. ViewEncapsulation.None
    • No view encapsulation​
  3. ViewEncapsulation.Native
    • ​view encapsulation using shadow dom

 

Directives


import {Directive, ElementRef} from 'angular2/angular2';

@Directive({
	selector: '[bold]'
})

export class BoldDirective {
	constructor(el: ElementRef) {
		el.nativeElement.style.fontWeight = 'bold';
	}
}
<div class="myapp">
    <p bold> Hello world </p>
</div>

Dependency Injection

class Vehicle {
    constructor() {
        var engine = new Engine();
            engine.build();
    }
}


Constructor injection

class Vehicle {
    constructor(engine: Engine) {
            engine.build();
    }
}



class Engine {
    constructor(nutsNBolts: NutsNBolts) {
            nutsNBolts.fit();
    }
}



class NutsNBolts {
    constructor() {
    .....
    }
    fit() {

    }
}



new Vehicle(new Engine(new NutsNBolts()));


// Parent component

import {Component} from 'angular2/angular2';
import {Bootstrap} from 'angular2/angular2';
import {UserService} from './userservice.js';

@Component({ template: `<div> My App </div>` ... })

export class App {
    constructor() {
     
    }
}
 
Bootstrap(App,[UserService]); //UserService available in entire app.



//Child component                                   

import {Component} from 'angular2/angular2';   
import {UserService} from './userservice.js';                          
...                                                                           
                                                                      
@Component({ ... })                                                      

export class SomeComponent {
    constructor(user: UserService) {  //Singleton 
        user.dosomething();    
    }
}

Global Injection

By default all objects injected are singleton

// parent.ts  -- parent component
import {RoutineService} from './componentservice';
import {Child} from './child';

@Component({
    ......
    template: `<div>Parent component  <child></child> </div>`,
    directives: [Child]
    providers: [RoutineService]
//    viewProviders: [RoutineService]
})

export class App {
    constructor(routineservice: RoutineService) {
         routineservice.addRoutine(["eat", "sleep", "code"]);
    }
}
//child.ts -- child component                                   

import {RoutineService} from './routineservice.js';
                                                                                                                                              
@Component({ 
    selector: 'child',
    template `<p> I am child component </p>`
})                                                      

export class SomeComponent {
    constructor(routineservice: RoutineService) {  //Singleton 
        routineservice.deleteRoutine(["sleep"]);    
    }
}

Component level injection

PIPES

<!-- Date PIPE --!>

<p>{{date | date:'mediumDate'}}</p>
    <!-- Sep 1, 2015 --!>
<p>{{date | date:'yMMMMd'}}</p>
    <!-- September 1, 2015 --!>
<p>{{date | date:'shortTime'}}</p>
    <!-- 3:50 pm --!>


<!-- CURRENCY PIPE --!>

<p>{{43 | currency: 'USD' : true}}</p>
    <!-- $43 --!>
<p>{{43 | currency: 'USD' : true : '2.2'}} </p>
    <!--$43.00 --!>
<p>{{43 | currency: 'USD' : true : '3.3'}} </p>
     <!--$043.000 --!>

Angular2 inbuilt pipes

  • currency
  • date
  • uppercase
  • json
  • limitTo
  • lowercase
  • orderBy
  • filter
  • async
  • decimal
  • percent
  • number

Async Pipes

@Component({
    template: 
    `
        <div>
             <h1>My Component </h1>
             This component is ... {{lazydata | async}}
        </div>
    `
});


export class App{
    lazyData: Promise<string> = null;

    constructor() {
        this.lazyData =  new Promise<string>((resolve: any, reject: any) => {
                                setTimeout(()=> resolve("lazy"), 3000);
                            })        
    }
}    

Async pipe also works with Observables.

Async calls

  • Http calls return observables.
  • Observable is of a love child of Promise and collection/set
  • Observable is alias to RxJs Observable
  • Rxjs is reactive programming libraray
import {Http} from 'angular2/angular2';
import {Earthquake} from '../models/earthquake';

class EarthquakeService() {
    .
    .
    .
    
    getEarthquakeData(callback:(data: any[]) => void) {
    
      this.http.get('http://earthquake-report.com/feeds/recent-eq?json')
    
    	    .map((response: any) => {
    	        return response.json();
    	    })
    
    	    .map((jsonData: any[]) => {
    		jsonData.forEach((item) => {
    	            this.earthquakeData.push(new Earthquake(item));
    		});
    		return this.earthquakeData;
    				
    	    })
    
    	    .subscribe(
    		(data:any) => callback(this.earthquakeData), //OnNext
    		(err:any)  => console.log(err),              //OnError
    		() => console.log("call completed")          //OnComplete
    	    );
    	}
    }
}

References

  • http://blog.thoughtram.io
  • http://angularconnect.com/sessions
  • https://angular.io
  • https://disqus.com/home/forum/victorsavkinsblog
  • https://github.com/abhishek-agashe/angular2-playground

 

?

Angular 5 Advance webinars 04

By Tarun Sharma

Angular 5 Advance webinars 04

  • 640