Creating an Attribute Directive in Angular

basic-highlight.directive.ts

import {Directive, ElementRef, OnInit} from '@angular/core';

@Directive({
  selector: '[appBasicHighlight]'
})
export class BasicHighlightDirective implements OnInit {

  constructor(private elementRef: ElementRef) {
  }

  ngOnInit(): void {
    this.elementRef.nativeElement.style.backgroundColor = 'red';
  }

}

better-highlight.directive.ts

Using the Renderer to build a Better Attribute Directive

import {Directive, ElementRef, OnInit, Renderer2} from '@angular/core';

@Directive({
  selector: '[appBetterHighlight]'
})
export class BetterHighlightDirective implements OnInit {

  constructor(private elementRef: ElementRef, private renderer: Renderer2) {
  }

  ngOnInit(): void {
    // use this in environments that DOM is not directly accessible like cordova apps
    this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'blue');
  }

}

app.component.html

<p appBasicHighlight>Hello World</p>
<p appBetterHighlight>Hello World 2</p>

References
https://github.com/mhdr/AngularSamples/tree/master/016/my-app

Understanding the Component Lifecycle in Angular

ngOnChanges() Respond when Angular (re)sets data-bound input properties. The method receives a SimpleChanges object of current and previous property values.

Called before ngOnInit() and whenever one or more data-bound input properties change.

ngOnInit() Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component’s input properties.

Called once, after the first ngOnChanges().

ngDoCheck() Detect and act upon changes that Angular can’t or won’t detect on its own.

Called during every change detection run, immediately after ngOnChanges() and ngOnInit().

ngAfterContentInit() Respond after Angular projects external content into the component’s view / the view that a directive is in.

Called once after the first ngDoCheck().

ngAfterContentChecked() Respond after Angular checks the content projected into the directive/component.

Called after the ngAfterContentInit() and every subsequent ngDoCheck().

ngAfterViewInit() Respond after Angular initializes the component’s views and child views / the view that a directive is in.

Called once after the first ngAfterContentChecked().

ngAfterViewChecked() Respond after Angular checks the component’s views and child views / the view that a directive is in.

Called after the ngAfterViewInit() and every subsequent ngAfterContentChecked().

ngOnDestroy() Cleanup just before Angular destroys the directive/component. Unsubscribe Observables and detach event handlers to avoid memory leaks.

Called just before Angular destroys the directive/component.

References
https://angular.io/guide/lifecycle-hooks

Projecting Content into Components with ng-content in Angular

Content projection is a pattern in which you insert, or project, the content you want to use inside another component. For example, you could have a Card component that accepts content provided by another component.

Single-slot content projection

server.component.html

<h2>Title</h2>
<p>
  <ng-content></ng-content>
</p>

app.component.html

<app-server>
  <p><i>Hello World</i></p>
</app-server>

Multi-slot content projection

import { Component } from '@angular/core';

@Component({
  selector: 'app-zippy-multislot',
  template: `
    <h2>Multi-slot content projection</h2>

    Default:
    <ng-content></ng-content>

    Question:
    <ng-content select="[question]"></ng-content>
  `
})
export class ZippyMultislotComponent {}
<app-zippy-multislot>
  <p question>
    Is content projection cool?
  </p>
  <p>Let's learn about content projection!</p>
</app-zippy-multislot>

References
https://github.com/mhdr/AngularSamples/tree/master/015/my-app
https://blog.angular-university.io/angular-ng-content/
https://angular.io/guide/content-projection

Getting Access to the Template DOM with @ViewChild in Angular

<div class="col-md-9">
  <input type="text" class="form-control" trim #nickName />
</div>
<div class="row">
  <button class="btn" (click)="onAddGift(nickName)">Add Gift</button>
</div>
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { Gift } from '../gift/gift.component';
 
@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  @ViewChild('nickName') nickName : ElementRef;
  userNickName: String = '';
 
  ..
 
  onAddGift(nickName: HTMLInputElement) {
    ..
    this.userNickName = nickName.nativeElement.value;
    ..
  }
  
}

References
http://www.jcombat.com/angular-5/local-references-in-angular

Local References in Angular

Note that we can only access local references in template, not on TypeScript code

<div class="col-md-9">
  <input type="text" class="form-control" trim #giftName />
</div>
<div class="row">
  <button class="btn" (click)="onAddGift(giftName)">Add Gift</button>
</div>
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { Gift } from '../gift/gift.component';
 
@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  userNickName: String = '';
 
  ..
 
  onAddGift(giftName: HTMLInputElement) {
    this.userGiftName = giftName.value;
    ..
    ..
  }
  
}

There is another way to do this using @ViewChild decorator.

References
http://www.jcombat.com/angular-5/local-references-in-angular

How to disable View (css) Encapsulation in Angular

A component’s styles can be encapsulated within the component’s host element so that they don’t affect the rest of the application.

import {ViewEncapsulation} from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'my-zippy',
  templateUrl: 'my-zippy.component.html',
  styles: [`
    .zippy {
      background: green;
    }
  `],
  encapsulation: ViewEncapsulation.None
})
class ZippyComponent {
  @Input() title: string;
}

References
https://angular.io/api/core/ViewEncapsulation
https://blog.thoughtram.io/angular/2015/06/29/shadow-dom-strategies-in-angular2.html

Binding to Custom Events of Component in Angular

server.component.ts

import {Component, OnInit, EventEmitter, Output} from '@angular/core';

@Component({
  selector: 'app-server',
  templateUrl: './server.component.html',
  styleUrls: ['./server.component.css']
})
export class ServerComponent implements OnInit {

  // this is now an event
  @Output() statusChanged = new EventEmitter<{ serverId: number, time: Date }>();
  status = 'offline';

  constructor() {
    setTimeout(() => {
      this.status = 'online';

      // fire event
      this.statusChanged.emit({serverId: 10, time: new Date()});
    }, 5000);
  }

  ngOnInit() {
  }

}

server.component.html

<p>
  server is {{status}}
</p>

app.component.ts

import {Component} from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  serverId;
  time;

  /**
   * receive data from event
   */
  onServerStatusChanged(data: { serverId: number, time: Date }) {
    this.serverId = data.serverId;
    this.time = data.time;
  }
}

app.component.html

<app-server (statusChanged)="onServerStatusChanged($event)"></app-server>
<p>Server Id : {{serverId}}, Date : {{time}}</p>

Assigning an Alias to Custom Events

@Output('nicolas') nic  = new EventEmitter<any>();

References
https://github.com/mhdr/AngularSamples/tree/master/013/my-app

Binding to Custom Properties of Components in Angular

server.component.ts

import {Component, OnInit, Input} from '@angular/core';

@Component({
  selector: 'app-server',
  templateUrl: './server.component.html',
  styleUrls: ['./server.component.css']
})
export class ServerComponent implements OnInit {

  // Now this is a property for component
  @Input() firstName: string;

  constructor() {
  }

  ngOnInit() {
  }

}

server.component.html

<p>
  Hello {{firstName}}
</p>

app.component.html

<app-server [firstName]="'Mahmood'"></app-server>
<app-server [firstName]="'Mehdi'"></app-server>

Assigning an Alias to Custom Properties

@Input('selectedHero') hero: string;

References
https://github.com/mhdr/AngularSamples/tree/master/012/my-app

Outputting Lists with ngFor on Angular

server.component.html

<p>
  server {{serverId}} is {{status}}
</p>

server.component.ts

import {Component, OnInit} from '@angular/core';
import {el} from '@angular/platform-browser/testing/src/browser_util';

@Component({
  selector: 'app-server',
  templateUrl: './server.component.html',
  styleUrls: ['./server.component.css']
})
export class ServerComponent implements OnInit {

  serverId = 0;
  status = 'offline';

  constructor() {

    // choose a number between 0 and 100
    this.serverId = Math.floor((Math.random() * 100) + 1);

    const random2 = Math.random();

    if (random2 > 0.5) {
      this.status = 'online';
    } else {
      this.status = 'offline';
    }

  }

  ngOnInit() {
  }

}

app.component.html

<input type="button" value="Add Server" (click)="onButtonClick()"/>
<app-server *ngFor="let s of servers"></app-server>

app.component.ts

import {Component} from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  servers = [];

  onButtonClick() {
    // just push a date to servers
    this.servers.push(new Date());
  }
}

Getting the Index when using ngFor

<app-server *ngFor="let s of servers;let i=index" [ngClass]="{'even':i%2==0}"></app-server>

References
https://github.com/mhdr/AngularSamples/tree/master/011/my-app