如何在 Angular 9 中将分页与可观察量和 AsyncPipe 相结合?



有一个来自API的产品列表。产品是分页的,用户可以切换到另一个页面。简化的模板如下所示:

<ul>
<li *ngFor="let product of products$ | async">{{ product.name }}</li>
</ul>
<button type="button" (click)="gotoPage(1)">1</button>
<button type="button" (click)="gotoPage(2)">2</button>

该组件如下所示:

export class ProductsComponent implements OnInit {
products$: Observable<Product[]>;
constructor(
private service: ProductService
) { }
ngOnInit() {
this.products$ = this.service.getAll({page: 1});
}
gotoPage(page: number): void {
this.products$ = this.service.getAll({page: page});
}
}

我的问题是: 这是更新Obersavble的正确方法吗?还是这会产生内存泄漏?

请注意:URL 不会更改,并且不应在分页更改时重新加载组件。

查看异步管道的源代码,您可以看到transform()函数内部:

if (obj !== this._obj) {
this._dispose();
return this.transform(obj as any);
}

如果有,如果它是一个新对象,则取消订阅以前的可观察对象。因此,您可以安全地以这种方式使用它。

你甚至没有订阅你的可观察量,所以我认为这里没有可能的内存泄漏,你只是得到一些数据,异步管道为你处理"转换"。

以防万一,当您订阅可观察量时,您需要添加几行代码才能正确取消订阅并防止内存泄漏:

ngUnsubscribe = new Subject();
myObservable: Observable<any>;
ngOnInit(){
this.myObservable.pipe(takeUntil(ngUnsubscribe))
.subscribe(...)
}
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}

一个主题,允许您在具有takeUntil的每个订阅上触发正确的取消订阅。

.next.complete是必要的,因为.unsubscribe没有按预期工作(在与 NGRX 一起工作时注意到它,并发现了一些谈论它的堆栈溢出线程(。

你总是可以防止内存泄漏,taketill + ngOnDestroy模式。

例如

声明一个新变量private onDestroy$: Subject<void> = new Subject<void>();

this.service.getAll({page: page})
.pipe(takeUntil(this.onDestroy$))
.subscribe(... do the necessary ...);

稍后,在 onDestroy(( 生命周期钩子中,您可以实现以下内容:

public ngOnDestroy(): void {
this.onDestroy$.next();
this.onDestroy$.complete() 
}

我们实际上所做的是声明一个新的可观察量;然后,通过将管道方法与 takeTill 一起使用,我们通知编译器当任何值出现在 onDestroy$ 中时,我们想要取消订阅可观察量,然后,通过将管道方法与 takeTill 一起使用,我们通知编译器,当任何值出现在 onDestroy$ 中时,我们想要取消订阅可观察量,从而防止内存泄漏。

众所周知,页面也伴随着每页的项目数。

我更喜欢将标准更改为行为主体,并将两个可观察量与mergeMap

class ProductService {
constructor() {
this.defaultCriteria = {
page: 0,
pageSize: 5
}
}
getAll(criteria) {
criteria = {
...this.defaultCriteria,
...criteria
}
return rxjs.of(Array(criteria.pageSize).fill(0).map((pr, index) => {
const num = index + criteria.pageSize * criteria.page + 1;
return {
id: num,
name: `Product ${num}`
}
}))
}
}
class ProductsComponent {
constructor(service) {
this.service = service;
this.page$ = new rxjs.BehaviorSubject(0);
this.products$ = null;
}
ngOnInit() {
this.products$ = this.page$.asObservable()
.pipe(rxjs.operators.mergeMap(page => this.service.getAll({
page
})))
}
gotoPage(page) {
this.page$.next(page);
}
}
const service = new ProductService();
const component = new ProductsComponent(service);
component.ngOnInit();
component.products$.subscribe(products => console.log(products));
component.gotoPage(1);
component.gotoPage(2);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>

最新更新