【问题标题】:How to combine a pagination with Observables and the AsyncPipe in Angular 9?如何在 Angular 9 中将分页与 Observables 和 AsyncPipe 结合起来?
【发布时间】:2020-05-29 12:28:21
【问题描述】:

有一个来自 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 不会更改,并且组件不应在分页更改时重新加载。

【问题讨论】:

    标签: angular asynchronous rxjs observable angular9


    【解决方案1】:

    查看Async管道的source,可以看到里面的transform()函数:

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

    如果有一个 Observable,如果它是一个新对象,它会取消订阅先前的 Observable。所以你以这种方式使用它是安全的。

    【讨论】:

    • 非常感谢。这清楚地表明,它将在转换后取消订阅。
    【解决方案2】:

    你甚至没有订阅你的 observable,所以我认为这里没有可能存在内存泄漏,你只需获取一些数据,然后异步管道为你处理“转换”。

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

    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 一起工作时发现了一些讨论它的 stackoverflow 线程)。

    【讨论】:

    • 感谢您的回答。如果我需要在组件中订阅,这与简单的:this.subscription = this.service.getAll().subscribe() init 和销毁:this.subscription.unsubscribe(); 相比有什么区别和优势?
    • 正如我所说,当只使用.unsubscribe() 时,会有一种奇怪的行为,有时不会完全取消订阅你的 observables。在我的工作中,我有内存泄漏,因为它运行不正常(找不到给我答案的线程抱歉)并且使用这个 apttern 解决了我的问题。 takeUntil 的优势在于,它只允许您在所有 observables 上使用 next &amp; complete (=unsubscribe) 的一个主题。
    【解决方案3】:

    takeUntil + ngOnDestroy 模式始终可以防止内存泄漏。

    例如,

    声明一个新变量private onDestroy$: Subject&lt;void&gt; = new Subject&lt;void&gt;();

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

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

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

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

    【讨论】:

    • 您还需要this.onDestroy$.complete() 才能正确退订。请参阅下面的答案
    【解决方案4】:

    我们都知道,页面也有每页的项目数。

    我更喜欢将标准更改为行为主题并将两个可观察对象与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);
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"&gt;&lt;/script&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-25
      • 2020-07-23
      • 1970-01-01
      • 2012-09-28
      • 1970-01-01
      • 2012-07-16
      • 2019-12-27
      相关资源
      最近更新 更多