结构型指令的职责是HTML布局。 它们塑造或重塑DOM的结构,比如添加、移除或维护这些元素。

像其它指令一样,你可以把结构型指令应用到一个宿主元素上。 然后它就可以对宿主元素及其子元素做点什么。

结构型指令非常容易识别。 在这个例子中,星号(*)被放在指令的属性名之前。

<div *ngIf="hero" >{{hero.name}}</div>

三个常用的内置结构型指令 —— NgIfNgForNgSwitch...。 

 1 <div *ngIf="hero" >{{hero.name}}</div>
 2 
 3 <ul>
 4   <li *ngFor="let hero of heroes">{{hero.name}}</li>
 5 </ul>
 6 
 7 <div [ngSwitch]="hero?.emotion">
 8   <app-happy-hero    *ngSwitchCase="'happy'"    [hero]="hero"></app-happy-hero>
 9   <app-sad-hero      *ngSwitchCase="'sad'"      [hero]="hero"></app-sad-hero>
10   <app-confused-hero *ngSwitchCase="'app-confused'" [hero]="hero"></app-confused-hero>
11   <app-unknown-hero  *ngSwitchDefault           [hero]="hero"></app-unknown-hero>
12 </div>

ngIf内幕

<div *ngIf="hero" >{{hero.name}}</div>

<ng-template [ngIf]="hero"> <div>{{hero.name}}</div> </ng-template>
  • *ngIf指令被移到了<ng-template>元素上。在那里它变成了一个属性绑定[ngIf]

  • <div>上的其余部分,包括它的class属性在内,移到了内部的<ng-template>元素上。

*ngFor内幕

 

<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">
  ({{i}}) {{hero.name}}
</div>

<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
  <div [class.odd]="odd">({{i}}) {{hero.name}}</div>
</ng-template>

 

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

有时我们会希望只有当特定的条件为真时才重复渲染一个 HTML 块。 你可能试过把*ngFor*ngIf放在同一个宿主元素上,但Angular 不允许。这是因为我们在一个元素上只能放一个结构型指令。

原因很简单。结构型指令可能会对宿主元素及其子元素做很复杂的事。当两个指令放在同一个元素上时,谁先谁后?NgIf优先还是NgFor优先?NgIf可以取消NgFor的效果吗? 如果要这样做,Angular 应该如何把这种能力泛化,以取消其它结构型指令的效果呢?

对这些问题,没有办法简单回答。而禁止多个结构型指令则可以简单地解决这个问题。 这种情况下有一个简单的解决方案:把*ngIf放在一个"容器"元素上,再包装进 *ngFor 元素。 这个元素可以使用ng-container,以免引入一个新的HTML层级。

NgSwitch 内幕

Angular 的 NgSwitch 实际上是一组相互合作的指令:NgSwitchNgSwitchCase 和 NgSwitchDefault

星号(*)语法比不带语法糖的形式更加清晰。 如果找不到单一的元素来应用该指令,可以使用<ng-container>作为该指令的容器。

 

 

 

<ng-template>指令

<ng-template>是一个 Angular 元素,用来渲染HTML。 它永远不会直接显示出来。 事实上,在渲染视图之前,Angular 会把<ng-template>及其内容替换为一个注释。

如果没有使用结构型指令,而仅仅把一些别的元素包装进<ng-template>中,那些元素就是不可见的。 在下面的这个短语"Hip! Hip! Hooray!"中,中间的这个 "Hip!"(欢呼声) 就是如此

<p>Hip!</p>
<ng-template>
  <p>Hip!</p>
</ng-template>
<p>Hooray!</p>

<ng-container> 的救赎

Angular的<ng-container>是一个分组元素,但它不会污染样式或元素布局,因为 Angular 压根不会把它放进 DOM 中。

<p>
  I turned the corner
  <ng-container *ngIf="hero">
    and saw {{hero.name}}. I waved
  </ng-container>
  and continued on my way.
</p>
 1 <div>
 2   Pick your favorite hero
 3   (<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>)
 4 </div>
 5 <select [(ngModel)]="hero">
 6   <ng-container *ngFor="let h of heroes">
 7     <ng-container *ngIf="showSad || h.emotion !== 'sad'">
 8       <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
 9     </ng-container>
10   </ng-container>
11 </select>

自定义结构性指令

component.ts

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

/**
 * Add the template content to the DOM unless the condition is true.
 */
@Directive({ selector: '[appUnless]'})
export class UnlessDirective {
  private hasView = false;

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

  @Input() 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;
    }
  }
}

component.html

<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 myUnless is set to false.
</p>

 

posted on 2017-11-03 18:13  jmw_jay  阅读(153)  评论(0编辑  收藏  举报