Angular使用技巧:NgIf 非常规用法

4,916 阅读4分钟

NgIf 是angular中使用频率非常高的指令,它的作用简单点说就是叛定一个表达式,并根据叛定结果匹配相应模板。

NgIf 基本用法

<div *ngIf="condition; then thenBlock else elseBlock"></div>
<ng-template #thenBlock>Content to render when condition is true.</ng-template>
<ng-template #elseBlock>Content to render when condition is false.</ng-template>

以上语法可以通俗的理解为当表达式condition叛定为true时,使用thenBlock模板内容,否则使用elseBlock模板内容。这是NgIf用法的一种,它还有其它一些更为简洁的用法,具体可见 NgIf API。API中对一些常规用法已经介绍的很详细了,基础的用法去看看文档就行了。。好了,结束,散场!

 

--- 500 年后 ---

 

今天我们主要来说个NgIf不太常规的用法。

 

模板中临时变量

在angular组件模板中,有时候我们可能会需要定义一个临时变量来提升性能,比如像下面这个组件:

HTML

<div *ngFor="let item of items || []">
    <span [title]="getDisplayText(item)">{{ getDisplayText(item) }}</span>
</div>

TS

@Component({
    selector: 'list',
    //...
})
export class ListComponent {

    @Input()
    items: Array<any>;

    @Input()
    displayMember: string;

    getDisplayText(item: any): any {
        return this.displayMember ? (item && item[this.displayMember]) : item;
    }
    
}

这是一个简单的列表组件,它绑定一个数组,并通过指定的displayMember来设置列表项要显示的文本内容。在组件模板html中,使用span标签包裹每项要显示的文本,并设置其title与显示文本相同。因为显示文本可以动态设置(displayMember),所以我们将相关逻辑放在getDisplayText方法中处理。

细心一点就会注意到,我们html模板中为了2处(标签包裹的内容及title)文本显示,调用了2次getDisplayText,但其实2次调用是重复的。这种问题如果在js代码里,定义一个临时变量问题就解决了,但是在angular的组件模板中并没有给我们提供定义临时变量的指令,这时候我们今天的主角NgIf就出场了。

NgIf多种用法中有一种是下面这样:

NgIf 其中一种用法

<div *ngIf="condition as value">{{value}}</div>

这句condition as value它可以将一个表达式as到一个变量,并且这个变量在其作用域(节点)范围内均有效,这相当于变相的为我们提供了在模板中定义临时变量的能力。

更新后的HTML

<div *ngFor="let item of items || []">
    <span *ngIf="getDisplayText(item) as text" [title]="text">{{ text }}</span>
</div>

现在我们通过这个变相的临时变量,将原有需要调用2次的方法减少到了仅调用1次,减少了不必要的性能损失,这在更为复杂的场景中会更加显著。 然而还不能高兴的太早,这里有一个问题,NgIf不管如何使用归根结底它还是会遵循自己的原则,就是叛定表达式,所以问题就是当getDisplayText(item)返回结果叛定为false时,相应的span节点并不会生成。如items其中一项为null0时,null0转换为boolean值均叛定为false,所以节点并不会生成,无法达到我们显示字符串"0"的预期。

解决方法其实很简单,我们可以将返回结果封装成对象,因为对象叛定结果将始终为true

再次更新后的HTML

<div *ngFor="let item of items || []">
    <span *ngIf="{ text: getDisplayText(item) } as temp" [title]="temp.text">{{ temp.text }}</span>
</div>

至此我们就有了一个相对完美的模板临时变量。

 

扩展

不仅仅是变相的模板临时变量,扩展一下它还可以做更多事,比如有时候我们可能会需要在模板的某个节点生成的时候做点什么,它可以为我们提供一个代码交互时机。

我们给上面的组件加个数据项绑定事件,将相应定制功能公开给组件使用者。

HTML

<div *ngFor="let item of items || []; let index=index;">
    <span *ngIf="getBindingContext(item, index) as ctx" [title]="ctx.text" [style.color]="ctx.textColor">{{ ctx.text }}</span>
</div>

TS

@Component({
    selector: 'list',
    //...
})
export class ListComponent {

    @Input()
    items: Array<any>;

    @Input()
    displayMember: string;

    getDisplayText(item: any): any {
        return this.displayMember ? (item && item[this.displayMember]) : item;
    }
    
    
    // 加入绑定事件
    @Output()
    itemBinding = new EventEmitter<{ item: any, index: number, textColor?: string }>();

    getBindingContext(item: any, index: number): { text: string, textColor?: string } {
        let args: { item: any, index: number, textColor?: string } = { item, index };
        this.itemBinding.emit(args);
        return ({ text: this.getDisplayText(item), textColor: args.textColor });
    }
    
}

现在组件使用者可以通过订阅itemBinding事件来定制每项的文本显示颜色了。

使用1

<list [items]="[{firstName: 'zhang', lastName: 'san'}, {firstName: 'li', lastName: 'si'}]" displayMember="lastName"
    (itemBinding)="$event.textColor=$event.item.firstName === 'li' ? 'red': null">
</list>

使用2

<list [items]="[0,1,2,3,4,5,6,7]" (itemBinding)="$event.textColor=$event.index%3 === 0 ? 'red': null">
</list>

 

散场!