NgIf

根据 expression 表达式的值,有条件的包含某个模板。

Conditionally includes a template based on the value of an expression.

查看"说明"...

NgModule

选择器

属性

属性说明
@Input()
ngIf: any
Write-only.
@Input()
ngIfThen: TemplateRef<NgIfContext>
Write-only.
@Input()
ngIfElse: TemplateRef<NgIfContext>
Write-only.

说明

ngIf 会对 expression 进行求值,如果为真,则在原地渲染 then 模板,否则渲染 else 模板。通常:

ngIf evaluates the expression and then renders the then or else template in its place when expression is truthy or falsy respectively. Typically the:

  • then 模板就是 ngIf 中内联的模板 —— 除非你指定了另一个值。

    then template is the inline template of ngIf unless bound to a different value.

  • else 模板是空白的 —— 除非你另行指定了。

    else template is blank unless it is bound.

常见用法

Most common usage

ngIf 指令最常见的用法是根据条件显示其内联模板,比如:

The most common usage of the ngIf directive is to conditionally show the inline template as seen in this example:

@Component({ selector: 'ng-if-simple', template: ` <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button> show = {{show}} <br> <div *ngIf="show">Text to show</div> ` }) class NgIfSimple { show: boolean = true; }
      
      
  1. @Component({
  2. selector: 'ng-if-simple',
  3. template: `
  4. <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button>
  5. show = {{show}}
  6. <br>
  7. <div *ngIf="show">Text to show</div>
  8. `
  9. })
  10. class NgIfSimple {
  11. show: boolean = true;
  12. }

通过 else 显示另一个模板

Showing an alternative template using else

如果 expression 为假时有必要显示一个模板,就可以用上述的 else 模板来进行绑定。 注意,else 绑定指向的是一个带有 #elseBlock 标签的 <ng-template> 元素。 该模板可以定义在此组件视图中的任何地方,但为了提高可读性,通常会放在 ngIf 的紧下方。

If it is necessary to display a template when the expression is falsy use the else template binding as shown. Note that the else binding points to a <ng-template> labeled #elseBlock. The template can be defined anywhere in the component view but is typically placed right after ngIf for readability.

@Component({ selector: 'ng-if-else', template: ` <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button> show = {{show}} <br> <div *ngIf="show; else elseBlock">Text to show</div> <ng-template #elseBlock>Alternate text while primary text is hidden</ng-template> ` }) class NgIfElse { show: boolean = true; }
      
      
  1. @Component({
  2. selector: 'ng-if-else',
  3. template: `
  4. <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button>
  5. show = {{show}}
  6. <br>
  7. <div *ngIf="show; else elseBlock">Text to show</div>
  8. <ng-template #elseBlock>Alternate text while primary text is hidden</ng-template>
  9. `
  10. })
  11. class NgIfElse {
  12. show: boolean = true;
  13. }

使用非内联的 then 模板

Using non-inlined then template

通常,then 模板就是 ngIf 的内联模板,不过你也可以通过绑定机制(就像 else 那样)来修改它。 因为 thenelse 都是绑定,因此可以在运行期间改变这个模板引用 —— 如下例所示。

Usually the then template is the inlined template of the ngIf, but it can be changed using a binding (just like else). Because then and else are bindings, the template references can change at runtime as shown in this example.

@Component({ selector: 'ng-if-then-else', template: ` <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button> <button (click)="switchPrimary()">Switch Primary</button> show = {{show}} <br> <div *ngIf="show; then thenBlock; else elseBlock">this is ignored</div> <ng-template #primaryBlock>Primary text to show</ng-template> <ng-template #secondaryBlock>Secondary text to show</ng-template> <ng-template #elseBlock>Alternate text while primary text is hidden</ng-template> ` }) class NgIfThenElse implements OnInit { thenBlock: TemplateRef<any>|null = null; show: boolean = true; @ViewChild('primaryBlock') primaryBlock: TemplateRef<any>|null = null; @ViewChild('secondaryBlock') secondaryBlock: TemplateRef<any>|null = null; switchPrimary() { this.thenBlock = this.thenBlock === this.primaryBlock ? this.secondaryBlock : this.primaryBlock; } ngOnInit() { this.thenBlock = this.primaryBlock; } }
      
      
  1. @Component({
  2. selector: 'ng-if-then-else',
  3. template: `
  4. <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button>
  5. <button (click)="switchPrimary()">Switch Primary</button>
  6. show = {{show}}
  7. <br>
  8. <div *ngIf="show; then thenBlock; else elseBlock">this is ignored</div>
  9. <ng-template #primaryBlock>Primary text to show</ng-template>
  10. <ng-template #secondaryBlock>Secondary text to show</ng-template>
  11. <ng-template #elseBlock>Alternate text while primary text is hidden</ng-template>
  12. `
  13. })
  14. class NgIfThenElse implements OnInit {
  15. thenBlock: TemplateRef<any>|null = null;
  16. show: boolean = true;
  17.  
  18. @ViewChild('primaryBlock')
  19. primaryBlock: TemplateRef<any>|null = null;
  20. @ViewChild('secondaryBlock')
  21. secondaryBlock: TemplateRef<any>|null = null;
  22.  
  23. switchPrimary() {
  24. this.thenBlock = this.thenBlock === this.primaryBlock ? this.secondaryBlock : this.primaryBlock;
  25. }
  26.  
  27. ngOnInit() { this.thenBlock = this.primaryBlock; }
  28. }

把条件结果保存在变量中

Storing conditional result in a variable

一种常见的需求模式为:我们要显示来自同一个对象的一组属性。如果该对象是 undefined,那么我们就不得不使用安全遍历操作符 ?. 来防止引用到空对象。 尤其是在使用 async 管道等待异步数据时,例如:

A common pattern is that we need to show a set of properties from the same object. If the object is undefined, then we have to use the safe-traversal-operator ?. to guard against dereferencing a null value. This is especially the case when waiting on async data such as when using the async pipe as shown in following example:

Hello {{ (userStream|async)?.last }}, {{ (userStream|async)?.first }}!
      
      Hello {{ (userStream|async)?.last }}, {{ (userStream|async)?.first }}!
    

上面这个例子中有一系列低效代码:

There are several inefficiencies in the above example:

  • 我们在 userStream 上创建了多个订阅。每个 async 管道都有一个,比如上面这个例子中就用了两个。

    We create multiple subscriptions on userStream. One for each async pipe, or two in the example above.

  • 在等待异步数据到来的时候,我们没法显示一个备用视图(如 loading)。

    We cannot display an alternative screen while waiting for the data to arrive asynchronously.

  • 我们不得不使用安全遍历操作符 ?. 来访问属性,太繁琐了。

    We have to use the safe-traversal-operator ?. to access properties, which is cumbersome.

  • 我们不得不把 async 管道放进圆括号里。

    We have to place the async pipe in parenthesis.

更好的方式是使用 ngIf,并把该条件的结果存到局部变量里,例如:

A better way to do this is to use ngIf and store the result of the condition in a local variable as shown in the the example below:

@Component({ selector: 'ng-if-let', template: ` <button (click)="nextUser()">Next User</button> <br> <div *ngIf="userObservable | async as user; else loading"> Hello {{user.last}}, {{user.first}}! </div> <ng-template #loading let-user>Waiting... (user is {{user|json}})</ng-template> ` }) class NgIfAs { userObservable = new Subject<{first: string, last: string}>(); first = ['John', 'Mike', 'Mary', 'Bob']; firstIndex = 0; last = ['Smith', 'Novotny', 'Angular']; lastIndex = 0; nextUser() { let first = this.first[this.firstIndex++]; if (this.firstIndex >= this.first.length) this.firstIndex = 0; let last = this.last[this.lastIndex++]; if (this.lastIndex >= this.last.length) this.lastIndex = 0; this.userObservable.next({first, last}); } }
      
      
  1. @Component({
  2. selector: 'ng-if-let',
  3. template: `
  4. <button (click)="nextUser()">Next User</button>
  5. <br>
  6. <div *ngIf="userObservable | async as user; else loading">
  7. Hello {{user.last}}, {{user.first}}!
  8. </div>
  9. <ng-template #loading let-user>Waiting... (user is {{user|json}})</ng-template>
  10. `
  11. })
  12. class NgIfAs {
  13. userObservable = new Subject<{first: string, last: string}>();
  14. first = ['John', 'Mike', 'Mary', 'Bob'];
  15. firstIndex = 0;
  16. last = ['Smith', 'Novotny', 'Angular'];
  17. lastIndex = 0;
  18.  
  19. nextUser() {
  20. let first = this.first[this.firstIndex++];
  21. if (this.firstIndex >= this.first.length) this.firstIndex = 0;
  22. let last = this.last[this.lastIndex++];
  23. if (this.lastIndex >= this.last.length) this.lastIndex = 0;
  24. this.userObservable.next({first, last});
  25. }
  26. }

注意:

Notice that:

  • 我们只用了一个 async 管道,因此也只会进行一次订阅。

    We use only one async pipe and hence only one subscription gets created.

  • ngIfuserStream|async 的结果保存在了局部变量 user 中。

    ngIf stores the result of the userStream|async in the local variable user.

  • 局部变量 user 可以反复绑定 —— 这很高效。

    The local user can then be bound repeatedly in a more efficient way.

  • 不需要使用安全遍历操作符 ?. 来访问属性,因为 ngIf 只有在 userStream 有数据时才会显示内容。

    No need to use the safe-traversal-operator ?. to access properties as ngIf will only display the data if userStream returns a value.

  • 在等待数据到达时,我们可以显示一个代用模板。

    We can display an alternative template while waiting for the data.

语法

Syntax

简单形式:

Simple form:

  • <div *ngIf="condition">...</div>
  • <ng-template [ngIf]="condition"><div>...</div></ng-template>

带有 else 块的形式:

Form with an else block:

<div *ngIf="condition; else elseBlock">...</div> <ng-template #elseBlock>...</ng-template>
      
      <div *ngIf="condition; else elseBlock">...</div>
<ng-template #elseBlock>...</ng-template>
    

带有 thenelse 块的形式:

Form with a then and else block:

<div *ngIf="condition; then thenBlock else elseBlock"></div> <ng-template #thenBlock>...</ng-template> <ng-template #elseBlock>...</ng-template>
      
      <div *ngIf="condition; then thenBlock else elseBlock"></div>
<ng-template #thenBlock>...</ng-template>
<ng-template #elseBlock>...</ng-template>
    

保存到局部变量的形式:

Form with storing the value locally:

<div *ngIf="condition as value; else elseBlock">{{value}}</div> <ng-template #elseBlock>...</ng-template>
      
      <div *ngIf="condition as value; else elseBlock">{{value}}</div>
<ng-template #elseBlock>...</ng-template>
    

静态方法

Assert the correct type of the expression bound to the ngIf input within the template.

static ngTemplateGuard_ngIf<E>(dir: NgIf, expr: E): expr is NonNullable<E>
      
      static ngTemplateGuard_ngIf<E>(dir: NgIf, expr: E): expr is NonNullable<E>
    
参数
dir NgIf
expr E
返回值

expr is NonNullable<E>

The presence of this method is a signal to the Ivy template type check compiler that when the NgIf structural directive renders its template, the type of the expression bound to ngIf should be narrowed in some way. For NgIf, it is narrowed to be non-null, which allows the strictNullChecks feature of TypeScript to work with NgIf.