模板表达式操作符

管道操作符 AsyncPipe

Angular 管道对像这样的小型转换来说是个明智的选择。 管道是一个简单的函数,它接受一个输入值,并返回转换结果。
[json](https://angular.cn/api/common/JsonPipe) 管道对调试绑定特别有用:

  1. <div>{{currentHero | json}}</div>

安全导航操作符 ( ?. ) 和空属性路径

  • 表达式会在它遇到第一个空值的时候跳出。 显示是空的,但应用正常工作,而没有发生错误。
  • 在像 [a](https://angular.cn/api/router/RouterLinkWithHref)?.b?.c?.d 这样的长属性路径中,它工作得很完美

    1. <div>The null hero's name is {{nullHero?.name}}</div>

    非空断言操作符(!)

  • 如果你打开了严格控制检测,那就要用到这个模板操作符,而其它情况下则是可选的。

  • 它只是告诉 TypeScript 的类型检查器对特定的属性表达式,不做 “严格空值检测”。

也就是说,null和undefined在常规模式下,是其它所有类型的子类型,变量可不赋值或任意赋值为null或undefined;如果使用严格空值检测,只要类型声明没有声明为undefined/null(包括联合声明类型),就不能赋值为null/undefined。
因此,在开启严格控制检测的情况下:

如果无法在运行类型检查器期间确定变量是否 nullundefined,则会抛出错误。

这就引出了非空断言操作符:

通过应用后缀非空断言运算符!来告诉类型检查器不要抛出错误。

在tslint.json中配置no-none-null-assertion,不允许非空断言

  1. // Compiled with --strictNullChecks
  2. let x: number;
  3. let y: number | undefined;
  4. let z: number | null | undefined;
  5. x = 1; // Ok
  6. y = 1; // Ok
  7. z = 1; // Ok
  8. x = undefined; // Error
  9. y = undefined; // Ok
  10. z = undefined; // Ok
  11. x = null; // Error
  12. y = null; // Error
  13. z = null; // Ok
  14. x = y; // Error
  15. x = z; // Error
  16. y = x; // Ok
  17. y = z; // Error
  18. z = x; // Ok
  19. z = y; // Ok
  1. <div *ngIf="hero">
  2. The hero's name is {{hero!.name}}
  3. </div

计算属性

does angular have the “computed property” feature like in vue.js? 最高赞

组件交互

1. 常规输入属性@Input

2. setter截听@Input

3. ngOnChanges

当需要监视多个、交互式输入属性的时候,本方法比用属性的 setter 更合适。

4. 子传父@Output

5. 通过服务传递 (可以兄弟组件之间)

—- 以下少用

6. 父组件通过模板变量#xxx控制子组件(只能在html模板中)

7. 父组件的类访问子组件(在ts中)

当父组件类需要这种访问时,可以把子组件作为 ViewChild,注入到父组件里面。
单向数据流

  1. import { AfterViewInit, ViewChild } from '@angular/core';
  2. import { Component } from '@angular/core';
  3. import { CountdownTimerComponent } from './countdown-timer.component';
  4. @Component({
  5. selector: 'app-countdown-parent-vc',
  6. template: `
  7. <h3>Countdown to Liftoff (via ViewChild)</h3>
  8. <button (click)="start()">Start</button>
  9. <button (click)="stop()">Stop</button>
  10. <div class="seconds">{{ seconds() }}</div>
  11. <app-countdown-timer></app-countdown-timer>
  12. `,
  13. styleUrls: ['../assets/demo.css']
  14. })
  15. export class CountdownViewChildParentComponent implements AfterViewInit {
  16. @ViewChild(CountdownTimerComponent)
  17. private timerComponent: CountdownTimerComponent;
  18. seconds() { return 0; }
  19. ngAfterViewInit() {
  20. // Redefine `seconds()` to get from the `CountdownTimerComponent.seconds` ...
  21. // but wait a tick first to avoid one-time devMode
  22. // unidirectional-data-flow-violation error
  23. setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
  24. }
  25. start() { this.timerComponent.start(); }
  26. stop() { this.timerComponent.stop(); }
  27. }

[ngAfterViewInit()](https://angular.cn/api/forms/NgForm#ngAfterViewInit) 生命周期钩子是非常重要的一步。被注入的计时器组件只有在 Angular 显示了父组件视图之后才能访问,所以它先把秒数显示为 0.
然后 Angular 会调用 ngAfterViewInit 生命周期钩子,但这时候再更新父组件视图的倒计时就已经太晚了。Angular 的单向数据流规则会阻止在同一个周期内更新父组件视图。应用在显示秒数之前会被迫再等一轮。
使用 setTimeout() 来等下一轮,然后改写 seconds() 方法,这样它接下来就会从注入的这个计时器组件里获取秒数的值。

ViewChild

访问组件

  1. @ViewChild(NzCarouselComponent, { static: true }) private nzCarousel: NzCarouselComponent;

访问templateRef

  1. @ViewChild('dot', { static: true }) dotRef: TemplateRef<any>;

访问指令

  1. @ViewChild(AdDirective, {static: true}) adHost: AdDirective;

动态组件

指令

AdDirective 注入了 [ViewContainerRef](https://angular.cn/api/core/ViewContainerRef) 来获取对容器视图的访问权,这个容器就是那些动态加入的组件的宿主。

  1. import { Directive, ViewContainerRef } from '@angular/core';
  2. @Directive({
  3. selector: '[ad-host]',
  4. })
  5. export class AdDirective {
  6. constructor(public viewContainerRef: ViewContainerRef) { }
  7. }

加载组件

ng-template

  1. <ng-template ad-host></ng-template>

解析组件

ComponentFactoryResolver

ViewContainerRef

entryComponent

  1. export class AdBannerComponent implements OnInit, OnDestroy {
  2. @Input() ads: AdItem[];
  3. currentAdIndex = -1;
  4. // AdDirective 曾在它的构造函数中注入了一个 ViewContainerRef。
  5. // 因此这个指令可以访问到这个你打算用作动态组件宿主的元素。
  6. @ViewChild(AdDirective, {static: true}) adHost: AdDirective;
  7. interval: any;
  8. constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
  9. ngOnInit() {
  10. this.loadComponent();
  11. this.getAds();
  12. }
  13. ngOnDestroy() {
  14. clearInterval(this.interval);
  15. }
  16. loadComponent() {
  17. this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
  18. const adItem = this.ads[this.currentAdIndex];
  19. const componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);
  20. const viewContainerRef = this.adHost.viewContainerRef;
  21. viewContainerRef.clear();
  22. const componentRef = viewContainerRef.createComponent(componentFactory);
  23. (<AdComponent>componentRef.instance).data = adItem.data;
  24. }
  25. getAds() {
  26. this.interval = setInterval(() => {
  27. this.loadComponent();
  28. }, 3000);
  29. }
  30. }
  1. entryComponents: [ HeroJobAdComponent, HeroProfileComponent ]