问题描述
我像这样调用服务中的 API
getItems(): Observable<{}> {
return this.http.get(<path>)
.pipe(
pluck('items'),);
}
以这种形式返回数据:
items: Object { key: value,key: value,key: value ...}
我像这样在我的组件中订阅了这个
items$!: any;
constructor(private readonly service: Service) { }
ngOnInit(): void {
this.service.getItems().subscribe(items => this.items$ = items);
}
并像这样在我的模板中显示它们
<div *ngFor="let item of items$ | keyvalue">
<mat-card>
<mat-card-header>
{{item.key}}
</mat-card-header>
<mat-card-content>
{{item.value}}
</mat-card-content>
</mat-card>
</div>
然而,我真正想要的是每隔几秒左右一次循环显示这些项目,而不是一次显示所有项目。我还想只进行一次 http 调用来完成此操作。我环顾四周,发现了解决方案的一些细节,但是,它通常至少涉及将对象转换为数组,然后可能使用某种 Subject 来观察订阅的原始 observable?这样做的干净方法是什么?谢谢。
解决方法
一种解决方案是将 API 中的数据转换为具有以下结构的数组:
const apiData = [{ a: 1 },{ b: 2 },{ c: 3 },{ d: 4 },{ e: 5 },{ f: 6 }];
接下来您可以使用以下使用 bufferCount
、concatMap
和 delay
运算符的代码
data = [];
from(apiData)
.pipe(
bufferCount(1),concatMap(objs => of(objs).pipe(delay(3000)))
)
.subscribe(e => {
this.data.push(e);
});
在模板上
<div *ngFor="let item of data">
{{item | json}} <-- tweak around with it to get exact details you need -->
</div>
,
创建一个每 5 秒发出一个值并与所有订阅者共享相同值的流。
item$: Observable<Item>;
ngOnInit(): void {
this.item$ = this.service.getItems().pipe(
switchMap(items => zip(
from(items),timer(0,5000)
)),map(([x,_]) => x),share()
);
}
订阅流。异步管道是使用 angular 做到这一点的最佳方式
<div>
<mat-card>
<mat-card-header>
{{(item$ | async).key}}
</mat-card-header>
<mat-card-content>
{{(item$ | async).value}}
</mat-card-content>
</mat-card>
</div>
更新 - 使用自定义运算符
function intervalArray<T>(milliseconds = 1000): OperatorFunction<T[],T> {
return s => s.pipe(
concatMap((a: T[]) => zip(
from(a),milliseconds)
).pipe(
map(([x,_]) => x)
))
);
}
ngOnInit(): void {
this.item$ = this.service.getItems().pipe(
intervalArray(5000),share()
);
}