在这种情况下,我的学生显示(阵列)与视图列表ngFor
:
<li *ngFor="#student of students">{{student.name}}</li>
这是美妙的,它更新每当我添加其他学生名单。
然而,当我给它一个pipe
来filter
由学生的名字,
<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 | exponentialStrength: factor}}
{{power | exponentialStrength: factor}}
power
和factor
是输入。
对于这个问题, "#student of students | sortByName:queryElem.value"
, students
和queryElem.value
是输入和管道sortByName
是无状态的/纯。 students
是一个数组(参考)。
- 当添加一个学生,阵列参考并不改变-
students
不改变-因此不执行无状态/纯管。 - 当某物被键入到滤波器输入,
queryElem.value
确实改变,因此,在执行无状态/纯管。
修复阵列问题的方法是每次添加一个学生的时间来改变阵列基准 - 即创建一个新的阵列每次添加一个学生的时间。 我们可以从这样做concat()
this.students = this.students.concat([{name: studentName}]);
虽然这个工作,我们addNewStudent()
方法不应该被实现,只是因为我们使用的管道以某种方式。 我们想用push()
添加到我们的阵列。
有状态管道
有状态的管具有状态-它们通常具有的特性,而不仅仅是一个transform()
方法。 他们可能需要即使他们的投入没有改变进行评估。 当我们指定一个管状态/非纯- pure: false
-那么只要角的变化检测系统检查的成分更改,但该组件采用的是状态管道,它将检查管道的输出,它的输入是否已经改变或不。
这听起来像我们所希望的,即使是低效率的,因为我们想,如果管道,甚至执行students
参考并没有改变。 如果我们简单地使管道状态,我们得到一个错误:
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的回答 ,“这个错误仅发生在开发模式(这是默认以β-0启用)。如果你打电话enableProdMode()
自举程序的时候,错误不会抛出。” 在对文档ApplicationRef.tick()
状态:
在开发模式中,蜱()还执行第二变更检测周期,以确保检测到没有进一步的变化。 如果附加的改变此第二周期期间拾取,在该应用绑定有副作用,不能在一个单一的变化检测通来解决。 在这种情况下,角抛出一个错误,因为一个角度应用程序只能有一个变化检测通期间,所有的变化检测必须完成。
在我们的场景中,我相信错误是虚假/误导。 我们有一个有状态的管道,并在每次调用时的输出可以改变 - 它可以有副作用,那也没关系。 管道后NgFor评估,所以它应该工作的罚款。
但是,我们不能真正与被抛出这个错误发展,所以一个解决方法是一个数组属性(即状态)添加到管道实施方案,并始终返回数组。 见@ pixelbits的回答这个解决方案。
然而,我们可以更加高效,正如我们所看到的,我们将不再需要在管道实现数组属性,我们将不再需要为双变化检测一种解决方法。
组件变化检测
默认情况下,每一个浏览器的事件,角度变化检测经过每一个部件,看它是否改变 - 输入和模板(?也许其他的东西)进行检查。
如果我们知道一个组件只取决于它的输入特性(与模板事件),以及输入性是不可改变的,我们可以用更有效的onPush
变化检测策略。 有了这个战略,而不是在每一个浏览器事件检查,一个组件只有当输入改变,当模板事件触发检查。 而且,很显然,我们没有得到Expression ... has changed after it was checked
错误使用此设置。 这是因为onPush
组件没有再次检查,直到它被“标记”( ChangeDetectorRef.markForCheck()
一次。 所以模板绑定和有状态的管输出被执行/只计算一次。 无国籍/纯管仍然不执行,除非它们的输入而改变。 所以,我们还在这里需要一个有状态的管道。
这是@EricMartinez建议的解决方案:状态管与onPush
变化检测。 见@ caffinatedmonkey对这个解决方案的答复。
注意,使用该溶液的transform()
方法不需要每次都返回相同的阵列。 我觉得有点奇怪,但:有状态的管道没有状态。 关于它的思考多一些......有状态的管道可能总是返回相同的阵列。 否则,它只能与使用onPush
在开发模式的组件。
所以,在这一切之后,我想我喜欢的@ Eric的和@ pixelbits的答案的组合:有状态的管道将返回相同的数组引用,与onPush
变化检测,如果组件允许它。 由于有状态管返回相同的数组引用,管仍然可以与未与被配置组件使用onPush
。
Plunker
这可能会成为一个角2成语:如果一个数组是喂养管,并且阵列可能会改变(即数组中的项目,而不是数组引用),我们需要使用有状态的管道。
正如埃里克·马丁内斯在评论中指出,加入pure: false
你Pipe
装饰和changeDetection: ChangeDetectionStrategy.OnPush
到您的Component
装饰器将解决您的问题。 这是一个工作plunkr。 更改为ChangeDetectionStrategy.Always
,也适用。 这里的原因。
据对管道angular2指南 :
管道是默认无状态。 我们必须声明一个管道是由设置状态pure
的财产@Pipe
装饰以false
。 这个设置告诉角度的变化检测系统检查该管的每个循环的输出,它的输入是否已经改变与否。
至于ChangeDetectionStrategy
,默认情况下,所有绑定检查每一个周期。 当一个pure: false
增加管,我相信从改变检测方法的变化CheckAlways
到CheckOnce
出于性能的考虑。 随着OnPush
中,组件绑定,只有当事件被触发输入属性发生变化或者检查。 有关改变探测器的一个重要组成部分的更多信息angular2
,请查看以下链接:
- 变化检测在角2
- https://github.com/angular/angular/issues/4746
- 变化检测维克多重新改造Savkin
演示Plunkr
你并不需要更改ChangeDetectionStrategy。 实现stateful管就足以使一切工作正常。
这是一个有状态的管道(没有其他变化作出):
@Pipe({
name: 'sortByName',
pure: false
})
export class SortByNamePipe {
tmp = [];
transform (value, [queryString]) {
this.tmp.length = 0;
// console.log(value, queryString);
var arr = value.filter((student)=>new RegExp(queryString).test(student.name));
for (var i =0; i < arr.length; ++i) {
this.tmp.push(arr[i]);
}
return this.tmp;
}
}
从角文档
纯不纯的管道
有两种类型的管道:纯不纯。 管道是默认纯。 每到目前为止你见过管道已纯净。 您通过其纯粹的标志设置为false管道不纯。 你可以让FlyingHeroesPipe不纯是这样的:
@Pipe({ name: 'flyingHeroesImpure', pure: false })
在这之前,了解纯不纯的区别,从一个纯粹的管道。
纯管角仅当它检测到纯变化的输入值执行纯管。 纯变化是要么改变到原始输入值(字符串,数字,布尔,符号)或改变的对象引用(日期,阵列,功能,对象)。
角忽略内(复合)对象中的变化。 如果你改变它不会调用纯管道输入一个月,添加到输入数组,或更新输入对象的属性。
这看似严格,但它也快。 对象引用检查比的差异,所以角可以迅速确定它是否可以跳过管执行和视图更新的深邃的检查速度更快快很多。
出于这个原因,一个纯粹的管道是最好的时候,你可以用变化检测策略生活。 当你不能,你可以使用不纯的管道。
一种解决方法:手动导入管的构造,并呼吁采用这种管变换方法
constructor(
private searchFilter : TableFilterPipe) { }
onChange() {
this.data = this.searchFilter.transform(this.sourceData, this.searchText)}
其实你甚至不需要管
而不是做纯:假的。 可以深复制并通过this.students = Object.assign([],NEW_ARRAY)替换组件中的值; 其中NEW_ARRAY是修改后的数组。
它适用于角6,应为其他角度版本的正常工作。
添加到管道额外的参数,和阵列更改之后更改它,甚至与纯管道,列表将被刷新
让项目的项目| 管:PARAM