组件可以在原生 HTML 元素中管理一小片区域的 HTML。从技术角度说,它就是一个带模板的指令。

属性型指令

用@input输入属性(别名)绑定指令的属性

ElementRef、HostListeners

image.png

  1. /* tslint:disable:member-ordering */
  2. import { Directive, ElementRef, HostListener, Input } from '@angular/core';
  3. @Directive({
  4. selector: '[appHighlight]'
  5. })
  6. export class HighlightDirective {
  7. constructor(private el: ElementRef) { }
  8. @Input() defaultColor: string;
  9. @Input('appHighlight') highlightColor: string;
  10. @HostListener('mouseenter') onMouseEnter() {
  11. this.highlight(this.highlightColor || this.defaultColor || 'red');
  12. }
  13. @HostListener('mouseleave') onMouseLeave() {
  14. this.highlight(null);
  15. }
  16. private highlight(color: string) {
  17. this.el.nativeElement.style.backgroundColor = color;
  18. }
  19. }
  1. <h1>My First Attribute Directive</h1>
  2. <h4>Pick a highlight color</h4>
  3. <div>
  4. <input type="radio" name="colors" (click)="color='lightgreen'">Green
  5. <input type="radio" name="colors" (click)="color='yellow'">Yellow
  6. <input type="radio" name="colors" (click)="color='cyan'">Cyan
  7. </div>
  8. <p [appHighlight]="color">Highlight me!</p>
  9. <p [appHighlight]="color" defaultColor="violet">
  10. Highlight me too!
  11. </p>

附录:为什么要加@Input?

  1. <p [appHighlight]="color">Highlight me!</p>
  • color 属性位于右侧的绑定表达式中,它属于模板所在的组件。 该模板和组件相互信任。因此 color 不需要 @[Input](https://angular.cn/api/core/Input) 装饰器。—— 组件的成员变量color
  • appHighlight 属性位于左侧,它引用了 HighlightDirective 中一个带别名的属性,它不是模板所属组件的一部分,因此存在信任问题。 所以,该属性必须带 @[Input](https://angular.cn/api/core/Input) 装饰器。 —— 是指令类中的@Input变量。

结构性指令

带*前缀和微语法

  • NgFor 指令在列表上循环,每个循环中都会设置和重置它自己的上下文对象上的属性。 这些属性包括但不限于 indexodd 以及一个特殊的属性名 $implicit(隐式变量)。
  • 这里并没有指定 let-hero 的上下文属性。它的来源是隐式的。
微语法 解语法糖后
*[ngFor](https://angular.cn/api/common/NgForOf)="let item of [1,2,3]" <ng-template [ngFor](https://angular.cn/api/common/NgForOf) let-item [[ngForOf](https://angular.cn/api/common/NgForOf)]="[1,2,3]">
*[ngFor](https://angular.cn/api/common/NgForOf)="let item of [1,2,3] as items; trackBy: myTrack; index as i" <ng-template [ngFor](https://angular.cn/api/common/NgForOf) let-item [[ngForOf](https://angular.cn/api/common/NgForOf)]="[1,2,3]" let-items="[ngForOf](https://angular.cn/api/common/NgForOf)" [ngForTrackBy]="myTrack" let-i="index">
*[ngIf](https://angular.cn/api/common/NgIf)="exp" <ng-template [[ngIf](https://angular.cn/api/common/NgIf)]="exp">
*[ngIf](https://angular.cn/api/common/NgIf)="exp as value" <ng-template [[ngIf](https://angular.cn/api/common/NgIf)]="exp" let-value="[ngIf](https://angular.cn/api/common/NgIf)">

模板输入变量

模板输入变量和引用变量具有各自独立的命名空间。let hero 中的 hero 和 #hero 中的 hero 并不是同一个变量

模板输入变量 模板引用变量
范围被限制在所重复模板的单一实例 引用变量引用的是它所附着到的元素、组件或指令。它可以在整个模板的任意位置访问

每个宿主元素上只能有一个结构型指令

例如:ngIf和ngFor不能在一个宿主元素

写一个结构型指令

  1. <p *appUnless="condition">Show this sentence unless the condition is true.</p>

TemplateRef 和 ViewContainerRef

像这个例子一样的简单结构型指令会从 Angular 生成的 元素中创建一个内嵌的视图,并把这个视图插入到一个视图容器中,紧挨着本指令原来的宿主元素

(译注:注意不是子节点,而是兄弟节点)。
你可以使用TemplateRef取得 的内容,并通过ViewContainerRef来访问这个视图容器。

  1. import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
  2. /**
  3. * Add the template content to the DOM unless the condition is true.
  4. */
  5. @Directive({ selector: '[appUnless]'})
  6. export class UnlessDirective {
  7. private hasView = false;
  8. constructor(
  9. private templateRef: TemplateRef<any>,
  10. private viewContainer: ViewContainerRef) { }
  11. @Input() set appUnless(condition: boolean) {
  12. if (!condition && !this.hasView) {
  13. this.viewContainer.createEmbeddedView(this.templateRef);
  14. this.hasView = true;
  15. } else if (condition && this.hasView) {
  16. this.viewContainer.clear();
  17. this.hasView = false;
  18. }
  19. }
  20. }
  • 如果条件为假,并且以前尚未创建过该视图,就告诉视图容器(ViewContainer)根据模板创建一个内嵌视图。
  • 如果条件为真,并且视图已经显示出来了,就会清除该容器,并销毁该视图

    改进

    用处应该不是很大,略。

NgTemplateOutlet

ng-container和ng-template的结合使用

文档demo

  1. 不带上下文
  2. let取用默认值let-name,即Context.$implicit
  3. let取用指定值let-person=”localSk”,即Context.localSk

    1. @Component({
    2. selector: 'ng-template-outlet-example',
    3. template: `
    4. <ng-container *ngTemplateOutlet="greet"></ng-container>
    5. <hr>
    6. <ng-container *ngTemplateOutlet="eng; context: myContext"></ng-container>
    7. <hr>
    8. <ng-container *ngTemplateOutlet="svk; context: myContext"></ng-container>
    9. <hr>
    10. <ng-template #greet><span>Hello</span></ng-template>
    11. <ng-template #eng let-name><span>Hello {{name}}!</span></ng-template>
    12. <ng-template #svk let-person="localSk"><span>Ahoj {{person}}!</span></ng-template>
    13. `
    14. })
    15. export class NgTemplateOutletExample {
    16. myContext = {$implicit: 'World', localSk: 'Svet'};
    17. }

    项目应用

    ```typescript

    1. {{ color[line] }}

  1. ```typescript
  2. <td>
  3. <ng-container *ngTemplateOutlet="editableCell; context: { color: color, line: 'code'}"></ng-container>
  4. </td>