Components

Components are the main building block for Angular applications.

Lifecycle hooks

Hook method Purpose Timing
ngOnChanges Responds when Angular sets or resets the input properties for the data binding. This method accepts the values of the current and previous attributesSimpleChangesobject inngOnInit()Called before and when the value of one or more of the bound input properties changes.
ngOnInit Initialize the directive/component after Angular first displays the data binding and set directive/component input properties. In the first roundngOnChanges()Call when done, call onlyAt a time.
ngDoCheck Detect, and react to changes that Angular can’t or won’t detect itself. Immediately following each change detectionngOnChanges()And when change detection is first performedngOnInit()After the call.
ngAfterContentInit Called when Angular projects external content into the view where the component view or directive resides. For the first time,ngDoCheck()And then it’s called, only once.
ngAfterContentChecked Called whenever Angular finishes checking what is projected into a component or directive. ngAfterContentInit()And every timengDoCheck()After the call
ngAfterViewChecked Called whenever Angular completes change detection for component views and subviews or views that contain the directive. ngAfterViewInit()And every timengAfterContentChecked()Call later.
ngOnDestroy Called and cleaned every time Angular destroys a directive/component. At presentsThe destructionCalled immediately before a directive or component.

Component interaction

Intercept input property changes with a setter

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

@Component({
  selector: 'app-name-child'.template: '<h3>"{{name}}"</h3>'
})
export class NameChildComponent {
  @Input(a)get name() :string { return this._name; }
  set name(name: string) {
    this._name = (name && name.trim()) || '<no name set>';
  }
  private _name = ' ';
}
Copy the code

Parent listens for child event

The child component exposes an EventEmitter property that it uses emits(catapult up) events when an event occurs. The parent component binds to this event property and responds when the event occurs.

The EventEmitter property of a child component is an Output property, usually with an @Output decorator.

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

@Component({
  selector: 'app-voter'.template: ` 

{{name}}

`
}) export class VoterComponent { @Input() name: string; @Output() voted = new EventEmitter<boolean> (); didVote =false; vote(agreed: boolean) { this.voted.emit(agreed); this.didVote = true; }}Copy the code

The parent VoteTakerComponent binds an event handler (oncommented ()) to respond to the child component’s event ($event) and updates a counter.

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

@Component({
  selector: 'app-vote-taker'.template: ` 

Should mankind colonize the Universe?

Agree: {{agreed}}, Disagree: {{disagreed}}

`
}) export class VoteTakerComponent { agreed = 0; disagreed = 0; voters = ['Narco'.'Celeritas'.'Bombasto']; onVoted(agreed: boolean) { agreed ? this.agreed++ : this.disagreed++; }}Copy the code

Parent interacts with child via local variable

import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';

@Component({
  selector: 'app-countdown-parent-lv'.template: ` 

Countdown to Liftoff (via local variable)

{{timer.seconds}}
`
.styleUrls: ['.. /assets/demo.css']})export class CountdownLocalVarParentComponent {}Copy the code

The parent component cannot use the child’s start and stop methods through data binding, nor can it access the child’s seconds property.

Place the local variable (#timer) in the (

) tag that represents the child component. The parent component’s template is referenced by the child, and all of its properties and methods can be accessed in the parent component’s template.

This example binds the buttons of the parent component to the start and stop methods of the child component and uses interpolation to display the seconds property of the child component.

Parent calls an @ViewChild()

 <mat-horizontal-stepper #stepper>
 </mat-horizontal-stepper>
 export class BaseInstallComponent {
   @ViewChild('stepper') stepper: MatHorizontalStepper;
 }
Copy the code
import { CountdownTimerComponent } from './countdown-timer.component';

@Component({
  selector: 'app-countdown-parent-vc'.template: ` 

Countdown to Liftoff (via ViewChild)

`
.styleUrls: ['.. /assets/demo.css']})export class CountdownViewChildParentComponent implements AfterViewInit { @ViewChild(CountdownTimerComponent) private timerComponent: CountdownTimerComponent; } Copy the code

Parent and children communicate via a service

MissionService
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable(a)export class MissionService {

  // Observable string sources
  private missionAnnouncedSource = new Subject<string> ();private missionConfirmedSource = new Subject<string> ();// Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();

  // Service message commands
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }

  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut); }}Copy the code

Dynamic Component

// tslint:disable: directive-selector
import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[adHost]',})export class AdDirective {
  constructor(public viewContainerRef: ViewContainerRef){}}Copy the code
export class AdBannerComponent implements OnInit.OnDestroy {
  @Input() ads: AdItem[];
  currentAdIndex = -1;
  @ViewChild(AdDirective, {static: true}) adHost: AdDirective;
  interval: any;

  constructor(private componentFactoryResolver: ComponentFactoryResolver){}ngOnInit() {
    this.loadComponent();
    this.getAds();
  }

  ngOnDestroy() {
    clearInterval(this.interval);
  }

  loadComponent() {
    this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
    const adItem = this.ads[this.currentAdIndex];

    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);

    const viewContainerRef = this.adHost.viewContainerRef;
    viewContainerRef.clear();

    const componentRef = viewContainerRef.createComponent<AdComponent>(componentFactory);
    componentRef.instance.data = adItem.data;
  }

  getAds() {
    this.interval = setInterval(() = > {
      this.loadComponent();
    }, 3000); }}Copy the code

Angular Elements

AppComponent

import { Component, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { PopupService } from './popup.service';
import { PopupComponent } from './popup.component';

@Component({
  selector: 'app-root'.template: `    `,})export class AppComponent {
  constructor(injector: Injector, public popup: PopupService) {
    // Convert `PopupComponent` to a custom element.
    const PopupElement = createCustomElement(PopupComponent, {injector});
    // Register the custom element with the browser.
    customElements.define('popup-element', PopupElement); }}Copy the code
PopupComponent

// tslint:disable: variable-name
import { Component, EventEmitter, HostBinding, Input, Output } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';

@Component({
  selector: 'my-popup'.template: ` Popup: {{message}}  `.animations: [
    trigger('state', [
      state('opened', style({transform: 'translateY(0%)'})),
      state('void, closed', style({transform: 'translateY(100%)'.opacity: 0})),
      transition(= > '* *', animate('100ms ease-in')),]],styles: [`:host { position: absolute; bottom: 0; left: 0; right: 0; background: #009cff; height: 48px; padding: 16px; display: flex; justify-content: space-between; align-items: center; border-top: 1px solid black; font-size: 24px; }button {border-radius: 50%; } `]})export class PopupComponent {
  @HostBinding('@state')
  state: 'opened' | 'closed' = 'closed';

  @Input(a)get message() :string { return this._message; }
  set message(message: string) {
    this._message = message;
    this.state = 'opened';
  }
  private _message: string;

  @Output()
  closed = new EventEmitter();
}
Copy the code
PopupService

import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
import { NgElement, WithProperties } from '@angular/elements';
import { PopupComponent } from './popup.component';


@Injectable(a)export class PopupService {
  constructor(private injector: Injector,
              private applicationRef: ApplicationRef,
              private componentFactoryResolver: ComponentFactoryResolver) {}

  // Previous dynamic-loading method required you to set up infrastructure
  // before adding the popup to the DOM.
  showAsComponent(message: string) {
    // Create element
    const popup = document.createElement('popup-component');

    // Create the component and wire it up with the element
    const factory = this.componentFactoryResolver.resolveComponentFactory(PopupComponent);
    const popupComponentRef = factory.create(this.injector, [], popup);

    // Attach to the view so that the change detector knows to run
    this.applicationRef.attachView(popupComponentRef.hostView);

    // Listen to the close event
    popupComponentRef.instance.closed.subscribe(() = > {
      document.body.removeChild(popup);
      this.applicationRef.detachView(popupComponentRef.hostView);
    });

    // Set the message
    popupComponentRef.instance.message = message;

    // Add to the DOM
    document.body.appendChild(popup);
  }

  // This uses the new custom-element method to add the popup to the DOM.
  showAsElement(message: string) {
    // Create element
    const popupEl = document.createElement('popup-element') as any;

    // Listen to the close event
    popupEl.addEventListener('closed'.() = > document.body.removeChild(popupEl));

    // Set the message
    popupEl.message = message;

    // Add to the DOM
    document.body.appendChild(popupEl); }}Copy the code

Templates

In Angular, a template is a chunk of HTML. Within a template, you can use special syntax to leverage many of Angular’s features.

Pipes

{{amount | currency: ‘EUR’}} will convert the amount to the euro

{{amount | currency: ‘EUR’ : ‘Euros’}} will be the second parameter (string’ Euros’) added to the output string

{{birthday | | date) uppercase}} is formatted after the date of the converted to uppercase characters

PipeTransform

import { Pipe, PipeTransform } from '@angular/core';
/* * Raise the value exponentially * Takes an exponent argument that defaults to 1. * Usage: * value | exponentialStrength:exponent * Example: * {{ 2 | exponentialStrength:10 }} * formats to: 1024 */
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
  transform(value: number, exponent? :number) :number {
    return Math.pow(value, isNaN(exponent) ? 1: exponent); }}Copy the code

Class binding

Binding Type Syntax Input Type Example Input Values
Single class binding [class.sale]=”onSale” boolean |undefined |nullg true, false
[class]=”classExpression” [class.sale]=”onSale” string “my-class-1 my-class-2 my-class-3”
{[key: string]: boolean | undefined | null} {foo: true, bar: false}
Array<string> [‘foo’, ‘bar’]

Directives

Built-in directives

Getting the index of *ngFor

<div *ngFor="let item of items; let i=index">{{i + 1}} - {{item.name}}</div>
Copy the code
<div *ngFor="let item of items; trackBy: trackByItems">  ({{item.id}}) {{item.name}}</div>
Copy the code

Attribute directives

Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor(private el: ElementRef) { } @HostListener('mouseenter') onMouseEnter() { this.highlight('yellow'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(null); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}Copy the code

structural directives

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

@Directive({ selector: '[appUnless]'})
export class UnlessDirective {
    //Inject TemplateRef and ViewContainerRef in the directive constructor as private variables.
    constructor(
      private templateRef: TemplateRef<any>,
      private viewContainer: ViewContainerRef){}//Add an appUnless @Input() property with a setter.
    @Input(a)set appUnless(condition: boolean) {
      if(! condition && !this.hasView) {
        this.viewContainer.createEmbeddedView(this.templateRef);
        this.hasView = true;
      } else if (condition && this.hasView) {
        this.viewContainer.clear();
        this.hasView = false; }}}Copy the code
<p *appUnless="condition" class="unless a">
  (A) This paragraph is displayed because the condition is false.
</p>

<p *appUnless=! "" condition" class="unless b">
  (B) Although the condition is true,
  this paragraph is displayed because appUnless is set to false.
</p>
Copy the code

Dependency injection

Angular Dependency injection

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

@Injectable({
  // declares that this service should be created
  // by the root application injector.
  providedIn: 'root',})export class HeroService {
  constructor(){}getHeroes() { returnHEROES; }}Copy the code

The @Injectable() decorator specifies that Angular can use this class in the DI system.

The metadata, providedIn: 'root', means that the HeroService is visible throughout the application.

Injecting services

constructor(heroService: HeroService)
Copy the code

Using services in other services

import the Logger service. Next, inject the Logger service in the HeroService constructor() by specifying private logger: Logger within the parentheses.

import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { Logger } from '.. /logger.service';

@Injectable({
  providedIn: 'root',})export class HeroService {

  constructor(private logger: Logger){}getHeroes() {
    this.logger.log('Getting heroes ... ');
    returnHEROES; }}Copy the code

Dependency providers

Defining providers

The following example is the class provider syntax for providing a Logger class in the providers array.

providers: [Logger]
Copy the code

Angular expands the providers value into a full provider object as follows.

[{ provide: Logger, useClass: Logger }]
Copy the code

Specifying an alternative class provider

[{ provide: Logger, useClass: EvenBetterLogger  }]
Copy the code
@Injectable(a)export class EvenBetterLogger extends Logger {
  constructor(private userService: UserService) { super(a); }log(message: string) {
    const name = this.userService.user.name;
    super.log(`Message to ${name}: ${message}`); }}Copy the code

Aliasing class providers

[ NewLogger,  
// Alias OldLogger reference to NewLogger  
{ provide: OldLogger, useExisting: NewLogger}]
Copy the code

Aliasing a class interface

providers: [{ provide: Parent, useExisting: forwardRef(() = > AlexComponent) }],
Copy the code

Injecting an object

[{ provide: Logger, useValue: SilentLogger }]
Copy the code
// An object in the shape of the logger service
function silentLoggerFn() {}

export const SilentLogger = {
  logs: ['Silent logger says "Shhhhh!" . Provided via "useValue"'].log: silentLoggerFn
};
Copy the code

Injecting a configuration object

export const HERO_DI_CONFIG: AppConfig = {
  apiEndpoint: 'api.heroes.com'.title: 'Dependency Injection'
};
Copy the code
providers: [  UserService,  { provide: APP_CONFIG, useValue: HERO_DI_CONFIG }],
Copy the code

Using an InjectionToken object

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

export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
Copy the code
providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
Copy the code
constructor(@Inject(APP_CONFIG) config: AppConfig) {  this.title = config.title; }Copy the code

Using factory providers

constructor(
  private logger: Logger,
  private isAuthorized: boolean){}getHeroes() {
  const auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
  this.logger.log(`Getting heroes for ${auth} user.`);
  return HEROES.filter(hero= > this.isAuthorized || ! hero.isSecret); }Copy the code
const heroServiceFactory = (logger: Logger, userService: UserService) = > {
  return new HeroService(logger, userService.user.isAuthorized);
};
Copy the code
// You inject both Logger and UserService into the factory provider
export let heroServiceProvider =
  { provide: HeroService,
    useFactory: heroServiceFactory,
    deps: [Logger, UserService]
  };
Copy the code

Reference Link -https://angular.cn/ Reference link -https://angular.io/

supplement

Is greater than the number >

Less than”