Angular 如何知道何时使用*ngFor在DOM中呈现项?

Angular 如何知道何时使用*ngFor在DOM中呈现项?,angular,rxjs,rxjs6,Angular,Rxjs,Rxjs6,我正在使用*ngFor显示一个消息列表,并希望将css类应用于最初呈现列表后添加到集合中的项目 i、 e.我不希望在视图最初加载时应用该类,但仅当messages集合获得新项时才应用该类 此集合的来源是一个可观察的服务,该服务可以随时更改 <div *ngFor="let message of thread.messages"> <div [class.fade-in-text]="threadWasLoaded"> {{ message.text

我正在使用
*ngFor
显示一个消息列表,并希望将css类应用于最初呈现列表后添加到集合中的项目

i、 e.我不希望在视图最初加载时应用该类,但仅当
messages
集合获得新项时才应用该类

此集合的来源是一个可观察的服务,该服务可以随时更改

<div *ngFor="let message of thread.messages">
    <div [class.fade-in-text]="threadWasLoaded">
        {{ message.text }}
    </div>
</div>
有可能我问的问题完全错了。请让我知道我的总体方法是否偏离基准。:-)

编辑:
我找到了一个解决问题的方法,通过在页面加载时抑制子动画,以不同的方式解决了这个问题。

文本淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入淡入?如果旧项目在文本中保持其
淡入状态
类,是否会导致问题?你能解决这个问题吗

如果是这种情况,只需在所有项目上保持
淡入文本类即可。只有新项目才会获得动画。但是Angular可能不知道某个项目是新的还是没有ngFor中的trackBy,因此它可能会将类删除/添加回旧项目,重新启动淡入动画。

编辑29/7-19: 根据评论更新Stackblitz:)

根据我的评论,我为您创建了一个有效的Stackblitz示例:

我在这里省略了生成名称的函数,但它在Stackblitz中。 对象=[]

  ngAfterViewInit() {
    this.fetchData();
    interval(5000).subscribe(() => this.addItem());

  }

  fetchData() {
    // creates mock data with a delay, simulating backend
    timer(2000).subscribe(() => this.objects = [{name: 'John Doe', initial: true}, {name: 'Marilyn Monroe', initial: true}])
  }

  addItem() {
    this.objects.push({name: this.generateName()});
  }
-


{{obj.name}


创建一个带有超时的可观察对象

<div *ngFor="let message of thread.messages">
    <div [class.fade-in-text]="delayTrue() | async">
        {{ message.text }}
    </div>
</div>

function delayTrue() {
   return new Observable(s => setTimeout(() => (s.next(true),s.complete()));
}

{{message.text}
函数delayTrue(){
返回新的可观察值(s=>setTimeout(()=>(s.next(true),s.complete());
}

删除您使用
threadwasload所做的一切
,错误应该会消失。

我知道这是通过另一种方式解决的,但对于未来的查找者来说,这是pairwise()操作符执行增量操作的完美用例:

  public  thread$ = this.dataService.thread$.pipe(
    startWith(null), // pairwise requires 2 emissions so we start it with a null
    pairwise(), // emit the last value and the current value in pairs
    map(([oThread, nThread]) => {
      if (oThread) { // only do this if there was a prior emission
        nThread.messages = nThread.messages.map(message => { // map the messages
          let isNew = false;
          if (!oThread.messages.find(m => m.id === message.id)) {
            // if they're not in the old array, they're new
            isNew = true;
          };
          return Object.assign({},message, {isNew});
        });
      }
      return nThread.messages;
    })
  );

还有一个亮点:

您是否能够以良好的方式访问初始数据?如何在初始对象上以真正的布尔值进行映射?如果不将其添加到以后的对象中,则属性将是未定义的。然后您可以使用ngClass直接修改DOM,这几乎总是不正确的。Angular的目的是修改DOMr你。我建议你在对象中添加一个属性,这样你就可以通过Angular应用该类。如果你想这样做的话,你还可以查看动画。谢谢大家的建议。我现在有时间回到这里。@LarsRødal-我确实可以访问初始数据,所以我尝试将属性应用到对象中问题是为了设置正确的值(true或false)我需要知道该项是否已在DOM中呈现,但我无法确定。@theMayer-我不打算直接操作DOM。将属性应用于原始项会很好,我只是还没有弄清楚如何确定这些项是否已呈现到DOM中,以设置适当的值。items a在中的某个阶段重新渲染到DOM-感谢您在这里所做的努力。在您的示例中,这是有效的,因为您将
initial
属性硬编码到项中。在我的示例中,我有一个observable,它有一个数组属性,包含所有项。当observable发出一个新值时,我得到了完整的集合,并且无法确定ermine可能是新的。这就是为什么我试图简单地设置一个全局布尔值来指示项目是否已渲染。这样,我就可以始终在该点之后设置正确的值。使用Object.assign将整个集合从可观察对象复制到现有数据对象上,我的解决方案应该仍然有效-初始加载ed元素的布尔值将设置为true(当数据对象未定义时,只需在初始加载时将其映射),而稍后出现的元素将不会有此键。我不确定这是否可行,但我已创建了一个说明此问题的解决方案。此外,我不再需要解决方案,因为我发现,通过在页面加载时抑制子动画,可以以不同的方式解决此问题。-这是它,按预期工作。可能对其他情况有用。:)谢谢你。我现在上班有点困难。TS正在抱怨缺少paren。尝试像这样使用它:
返回新的可观察对象(s=>setTimeout(()=>{s.next(true);s.complete();}))应该这样做吗?我看到了同样的行为-\是的,
淡入文本
是一个动画!保留该类的旧项不是问题。我试图利用trackBy函数,将动画应用于所有项目,但是,我不想在页面加载时将动画应用于项目,因此我正在寻找一种方法来确定是否有任何列表项目已实际呈现。在那之后,我知道所有未来的物品都应该得到这个等级。太好了。。!我是rxjs的新成员,因此感谢您的帮助…!:-)
<div *ngFor="let message of thread.messages">
    <div [class.fade-in-text]="delayTrue() | async">
        {{ message.text }}
    </div>
</div>

function delayTrue() {
   return new Observable(s => setTimeout(() => (s.next(true),s.complete()));
}
  public  thread$ = this.dataService.thread$.pipe(
    startWith(null), // pairwise requires 2 emissions so we start it with a null
    pairwise(), // emit the last value and the current value in pairs
    map(([oThread, nThread]) => {
      if (oThread) { // only do this if there was a prior emission
        nThread.messages = nThread.messages.map(message => { // map the messages
          let isNew = false;
          if (!oThread.messages.find(m => m.id === message.id)) {
            // if they're not in the old array, they're new
            isNew = true;
          };
          return Object.assign({},message, {isNew});
        });
      }
      return nThread.messages;
    })
  );