【问题标题】:Finer control over Angular rendering更好地控制 Angular 渲染
【发布时间】:2019-07-23 01:19:46
【问题描述】:

我遇到了一个很常见的情况:

在 NgRx 状态指示操作完成之前,我会显示一个忙等待微调器。

在模板中:

<my-loading-spinner *ngIf="loading$ | async"></my-loading-spinner>

在组件支持代码中:

this.loading$ = store.pipe(select(operationStatus), map(loadingFn));

...loadingFn 函数将operationState NgRx 状态值映射到布尔值。

这个视图的结果数据被加载到 NgRx 状态,就在 operationStatus 旁边,在 reducer 的同一个调用中:

return set(
        STATUS, EntityStatus.loadingSuccess, // the operationState
        myEntityAdapter.addAll(action.payload.tokens, state) // the data
      ) as MyEntityState;

这个结果数据是一个冗长的对象列表,它在组件中经过排序,然后通过具有非平凡内容的*ngFor 呈现。

所以我的问题随之而来也就不足为奇了:busy-wait spinner 在渲染的数据表出现在屏幕上之前就明显消失了。

是否可以监视任何类型的“某些特定数据已完成渲染”事件,以便我可以让我的忙等待微调器一直存在,直到数据表完全渲染?

2019.03.01 更新

我尝试了@ConnorsFan 建议的方法:我将模板更改为使用新的不可观察*ngIf="viewLoading" 初始化为public viewLoading = true; 然后在订阅正文中我将其设置为false:

this.dataRows.changes.subscribe(() => {
    this.viewLoading = false;
});

我尝试了这种方法,但没有任何作用;微调器仍然过早地隐藏起来。也许我没有正确应用食谱?我应该注意到这也引发了运行时错误,总是具有挑战性的ExpressionChangedAfterItHasBeenCheckedError上一个值:'ngIf: true'。当前值:'ngIf: false'

【问题讨论】:

  • 你能做类似*ngIf="(loading$ | async) &amp;&amp; !items" 的事情吗,其中 items 是ngFor 使用的数组。在项目被赋值之前执行任何排序工作。
  • 有趣的想法@cgTag;明天我会试一试,看看会发生什么。
  • 不幸的是,这对@ccTag 没有帮助;显然,表达式中的项目列表可用得太早了。

标签: angular ngrx busyindicator


【解决方案1】:

当您有由ngFor 循环生成的元素时:

<div #dataRow *ngFor="let item of items"> ... </div>

您可以通过ViewChildren 获取元素的QueryList,并通过订阅QueryList.changes 事件来通知元素已被渲染:

import { Component, ViewChildren, QueryList, ElementRef, AfterViewInit } from '@angular/core';
...    
export class MyComponent {

  @ViewChildren("dataRow") dataRows: QueryList<ElementRef>;

  dataRowsRendered: boolean;

  ngAfterViewInit() {
    this.dataRows.changes.subscribe(() => {
      // Do something after the data rows have been rendered
      console.log(`${this.dataRows.length} data rows have been rendered`);
      this.dataRowsRendered = true;
    });
  }

}

【讨论】:

  • 感谢您的建议!尝试了这种方法,但无济于事——请参阅我在上述问题中添加的注释。
  • 如果使用console.log(this.dataRows.length)QueryList.changes回调中报告了多少行?另外:我们能看到ngFor循环的相关部分吗?
【解决方案2】:

我认为@ConnorsFan 是正确答案。没想到用@ViewChildren()

你可以修改他的回答来更新loading$ observable。

@ViewChildren("dataRow") dataRows: QueryList<ElementRef>;

this.loading$ = combineLatest(
     store.pipe(select(operationStatus), map(loadingFn)),
     this.dataRows.changes.pipe(mapTo(this.dataRows.length === 0)
).pipe(map(([loading,rows])=>loading || rows));

我还建议在加载时将样式 display: none 添加到 ngFor 元素,然后根据 loading$ 将其删除。这将删除 QueryList 发出它已更改的显示。因此,当列表显示时,它应该已经存在于完全渲染的 DOM 中。

【讨论】:

    猜你喜欢
    • 2011-01-16
    • 1970-01-01
    • 2016-12-27
    • 2015-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-10
    • 2019-02-11
    相关资源
    最近更新 更多