📅  最后修改于: 2023-12-03 15:09:48.624000             🧑  作者: Mango
在 Angular 应用程序中,ngFor 是一个很常用的指令,用于在 HTML 模板中迭代数据。通常,ngFor 可以在同步情况下正常工作,但在异步情况下会遇到问题,例如从 API 加载数据等。为了解决这个问题,Angular 提供了一个异步管道,即异步管道 ngFor。
import { CommonModule } from '@angular/common';
ngForAsync.directive.ts:
import { Directive, Input, OnChanges, OnDestroy, SimpleChange, SimpleChanges, TemplateRef, ViewContainerRef } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { Observable, Subscription } from 'rxjs';
@Directive({
selector: '[ngForAsync]'
})
export class NgForAsyncDirective<T> implements OnChanges, OnDestroy {
@Input() ngForAsync: Observable<T[]> | undefined;
@Input() ngForAsyncTrackBy: ((index: number, item: T) => any);
@Input() ngForAsyncTemplate: TemplateRef<any>;
private items: T[] | undefined;
private subscription: Subscription | undefined;
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<any>, private asyncPipe: AsyncPipe) {
this.viewContainer.clear();
}
private updateView(items: T[]) {
this.viewContainer.clear();
items.forEach((item, index) => {
const childView = this.viewContainer.createEmbeddedView(this.ngForAsyncTemplate, {
$implicit: item,
index
});
childView.detectChanges();
});
}
private updateSubcription() {
if (this.subscription && !this.subscription.closed) {
this.subscription.unsubscribe();
}
this.subscription = this.ngForAsync?.subscribe(items => {
this.items = items;
this.updateView(items);
});
}
public ngOnChanges(changes: SimpleChanges) {
const ngForAsyncChange: SimpleChange = changes['ngForAsync'];
if (ngForAsyncChange) {
this.updateSubcription();
}
}
public ngOnDestroy() {
if (this.subscription && !this.subscription.closed) {
this.subscription.unsubscribe();
}
}
}
app.component.ts:
import { Component } from '@angular/core';
import { Observable, of } from 'rxjs';
@Component({
selector: 'app-root',
template: `
<ul>
<li *ngForAsync="let item of items$; trackBy: trackByFn; template: itemTemplate">{{ item.name }}</li>
</ul>
<ng-template #itemTemplate let-item let-i="index">
{{ i + 1 }} - {{ item.id }} - {{ item.name }}
</ng-template>
`
})
export class AppComponent {
items$: Observable<any[]> = of(
[
{
id: 1,
name: 'Item 1'
},
{
id: 2,
name: 'Item 2'
}
]
);
trackByFn(index: number, item: any) {
return item.id;
}
}
在上面的示例中,AppComponent 定义了一个 items$ Observable。这个 Observable 反映了从 API 加载数据的异步情况。items$ 传递到模板中的 *ngForAsync 中,传递另外两个参数,即 trackByFn 和 itemTemplate。注意,这里使用自定义的指令 *ngForAsync 而不是原生的 *ngFor。
最后,为 itemTemplate 定义了一个 ng-template。这个 ng-template 定义了要显示的内容。通过内置的 $implicit 和 index 变量,我们可以引用 item 和 index。
注意:在 *ngForAsync 中,我们必须设置跟踪函数 trackByFn。这个函数是用来跟踪每个列表项的,以避免不必要的重新渲染。我们可以选择某个唯一的值来跟踪每个列表项。在上面的示例中,我们使用了每个项的 id。