我有过滤器管道,用于过滤对象数组。过滤管按服务注入依赖关系。 服务具有模型数据filterService.data
。
如何仅在服务中的模型更改时才激活模板中的管道?
@Pipe({
name: 'filter',
})
export class FilterPipe implements PipeTransform {
constructor(private filterService: FilterService) {}
transform(array: any, start?: any, end?: any): any {}
}
用:
*ngFor="let item of response | filter"
默认情况下,角度管道是纯管道。它仅在任何输入参数更改时触发更新。这是您可以"激活"管道(更新其结果(的唯一方法。管道使用的任何依赖项服务都是内部的,与管道使用者无关,管道使用者负责在检测到馈送到管道的数据的更改后重新呈现其视图。
所以在你的例子中,只有当 Angular 检测到array
、start
或end
的变化时,它才会激活管道。同样,更改这些参数是控制管道重新应用的唯一方法。因此,在模型更改后使管道更新的正确方法是通过馈送到管道中的任何输入参数传播更改。然后在管道内部,您可以将转换服务应用于数据以生成所需的结果。不要向服务提供此流之外的任何数据,否则更改检测将永远不会赶上它。使管道依赖于这样的外部数据容易产生副作用,违反了纯管道必须是纯函数的规则。
假设如果要向过滤器添加更多排序标准,则应像这样修改管道:
transform(array: any[], start?: any, end?: any, orderBy?: string, descending?: boolean): any[] {}
为了确保对这些条件的任何更改,触发器都会通过传递的参数在管道上更新。
现在看array
:管道的第一个参数始终指定管道作用于的目标,而不是传递给过滤器服务的选项(通常是标量值(。更改此数组时,管道也应激活,但有一个问题。由于数组是通过引用传递的,因此即使您修改了源数组,如果更改到位,数组仍被视为相同,因此就管道而言,不会检测到任何更改。因此,当您更新源数组时,例如获取新的项目页面,如果要触发更新,请务必更改数组本身,而不仅仅是更改其内容:
array = [...array, newItem]; // NOT array.push(newItem);
坚持这条规则,那么你的管道就会表现得很好。
管道是自定义视图的便捷方法,但尽量不要过度使用它们。按照 Angular 文档的建议,将转换任务移动到组件渲染逻辑中(或更好地使用共享服务(:
Angular团队和许多经验丰富的 Angular 开发人员都非常强大 建议将筛选和排序逻辑移动到组件中 本身。
这样,您就可以更好地控制呈现数据的方式和时间,并且可能会提高效率,因为您可以过滤掉数据子集以获得最佳渲染,而不是像我们习惯的那样全部连接到昂贵的管道并返回。
快速解决方案 - 不纯管道,将随着每次更改而更新,即使它是不必要的:
@Pipe({
name: 'filter',
pure: false
})
但是这种解决方案在性能方面是不可接受的。
纯管道在更改值或参数后更新,但如果对数组的引用没有更改,则管道将不会更新。您可以尝试在更改数组后更新数组上的引用:response = [...response]
无需签入模板,您可以在服务中保留模型的状态,并仅在它满足条件时才应用转换。
@Pipe({
name: 'filter',
})
export class FilterPipe implements PipeTransform {
constructor(private filterService: FilterService) {}
transform(array: any, start?: any, end?: any): any {
if (filterService.applyTransform) {
// apply transformation
} else {
return array;
}
}
}