NgFor不使用Angular2中的Pipe更新数据

前端之家收集整理的这篇文章主要介绍了NgFor不使用Angular2中的Pipe更新数据前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在这种情况下,我使用ngFor显示一个学生列表(数组)到视图中:
<li *ngFor="#student of students">{{student.name}}</li>

它是美好的,它更新每当我添加其他学生到列表。

但是,当我给它一个管道过滤学生的名字,

<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>

它不会更新列表,直到我在过滤学生姓名字段中键入内容

这里有一个链接plnkr

Hello_world.html

<h1>Students:</h1>
<label for="newStudentName"></label>
<input type="text" name="newStudentName" placeholder="newStudentName" #newStudentElem>
<button (click)="addNewStudent(newStudentElem.value)">Add New Student</button>
<br>
<input type="text" placeholder="Search" #queryElem (keyup)="0">
<ul>
  <li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
</ul>

sort_by_name_pipe.ts

import {Pipe} from 'angular2/core';

@Pipe({
  name: 'sortByName'
})
export class SortByNamePipe {

  transform (value,[queryString]) {
    // console.log(value,queryString);
    return value.filter((student)=>new RegExp(queryString).test(student.name))
    // return value;
  }
}
为了完全理解问题和可能的解决方案,我们需要讨论角度变化检测 – 管道和组件。

管道更换检测

无状态/纯管道

默认情况下,管道是无状态/纯的。无状态/纯管道简单地将输入数据转换为输出数据。他们不记得任何东西,所以他们没有任何属性 – 只是一个transform()方法。因此,角度可以优化无状态/纯管道的处理:如果它们的输入不改变,则管道不需要在变化检测周期期间被执行。对于诸如{{power |指数强度:因子}},功率和因子是输入。

对于这个问题,“#学生的学生| sortByName:queryElem.value”,学生和queryElem.value是输入,管道sortByName是无状态/纯。学生是一个数组(参考)。

>添加学生时,数组引用不会更改 – 学生不会更改 – 因此无状态/纯管道不会执行。
>当有人输入到过滤器输入中时,queryElem.value会改变,因此无状态/纯管道被执行。

修复数组问题的一种方法是在每次添加学生时更改数组引用,即,每次添加学生时创建一个新数组。我们可以使用concat():

this.students = this.students.concat([{name: studentName}]);

虽然这个工作,我们的addNewStudent()方法不应该被实现一定的方式只是因为我们使用管道。我们想使用push()添加到我们的数组。

状态管道

状态管道具有状态 – 它们通常具有属性,而不仅仅是transform()方法。即使他们的输入没有改变,他们可能需要评估。当我们指定一个管道是有状态的/非纯的 – 纯:假 – 然后每当角度的变化检测系统检查一个组件的变化,该组件使用有状态的管道,它将检查管道的输出,它的输入是否已更改或不。

这听起来像我们想要的,即使它效率较低,因为我们想要管道执行,即使学生参考没有改变。如果我们只是使管道有状态,我们得到一个错误

EXCEPTION: Expression 'students | sortByName:queryElem.value  in HelloWorld@7:6' 
has changed after it was checked. PrevIoUs value: '[object Object],[object Object]'. 
Current value: '[object Object],[object Object]' in [students | sortByName:queryElem.value

根据@drewmoore’s answer,“这个错误只发生在dev模式(默认情况下是beta-0启用)。如果你调用enableProdMode()时引导应用程序,错误不会被抛出。 docs for ApplicationRef.tick()国家:

In development mode,tick() also performs a second change detection cycle to ensure that no further changes are detected. If additional changes are picked up during this second cycle,bindings in the app have side-effects that cannot be resolved in a single change detection pass. In this case,Angular throws an error,since an Angular application can only have one change detection pass during which all change detection must complete.

在我们的情况下,我相信错误是假的/误导。我们有一个有状态的管道,输出可以改变每次它被调用 – 它可以有副作用,这没关系。 NgFor是在管道之后进行评估的,所以它应该工作正常。

但是,我们不能真正开发这个错误被抛出,所以一个解决方法添加一个数组属性(即状态)到管道实现,并始终返回该数组。请参阅@ pixelbits的答案这个解决方案。

但是,我们可以更高效,我们将看到,我们不需要在管道实现中的array属性,我们不需要一个解决方法来进行双重更改检测。

组件更改检测

默认情况下,在每个浏览器事件,角度变化检测通过每个组件,看看它是否改变 – 输入和模板(和其他东西?)被检查。

如果我们知道一个组件只取决于它的输入属性(和模板事件),并且输入属性是不可变的,我们可以使用更高效的onPush变化检测策略。有了这个策略,而不是检查每个浏览器事件,只有当输入变化和模板事件触发时,才检查组件。并且,显然,我们没有得到的表情…改变后,检查这个设置的错误。这是因为onPush组件不会再次检查,直到它被“标记”(ChangeDetectorRef.markForCheck())再次。因此,模板绑定和有状态管道输出只执行/计算一次。无状态/纯管道仍然不被执行,除非它们的输入改变。所以我们还需要一个有状态的管道。

这是解决方案@EricMartinez建议:有状态管道与onPush变化检测。参见@ caffinatedmonkey的答案这个解决方案。

注意,使用这个解决方案,transform()方法不需要每次都返回相同的数组。我发现有点奇怪:有状态的管道没有状态。想想它更多…有状态的管道可能应该总是返回相同的数组。否则它只能在dev模式下与onPush组件一起使用。

所以,毕竟,我想我喜欢@ Eric和@ pixelbits的答案的组合:状态管道返回相同的数组引用,与onPush变化检测,如果组件允许它。由于有状态的管道返回相同的数组引用,所以管道仍然可以与未配置onPush的组件一起使用。

Plunker

这可能会成为一个Angular 2的成语:如果一个数组正在进给一个管道,并且数组可能改变(数组中的项,而不是数组引用),我们需要使用有状态的管道。

猜你在找的Angularjs相关文章