当前位置:  开发笔记 > 前端 > 正文

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

如何解决《NgFor不使用Angular2中的Pipe更新数据》经验,为你挑选了4个好方法。

在这种情况下,我正在向视图中显示学生列表(数组)ngFor:

  • {{student.name}}
  • 每当我将其他学生添加到列表中时,它都会更新.

    然而,当我给它一个pipefilter由学生的名字,

  • {{student.name}}
  • 在我在过滤学生姓名字段中输入内容之前,它不会更新列表.

    这是plnkr的链接.

    Hello_world.html

    Students:


    • {{student.name}}

    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;
        }
    }
    

    Mark Rajcok.. 144

    为了完全理解问题和可能的解决方案,我们需要讨论角度变化检测 - 用于管道和组件.

    管道变化检测

    无状态/纯管

    默认情况下,管道是无状态/纯粹的.无状态/纯管道只是将输入数据转换为输出数据.他们不记得任何东西,所以他们没有任何属性 - 只是一种transform()方法.因此,Angular可以优化无状态/纯管道的处理:如果它们的输入没有改变,则在变化检测循环期间不需要执行管道.对于管道,如{{power | exponentialStrength: factor}},powerfactor输入.

    对于这个问题,"#student of students | sortByName:queryElem.value",studentsqueryElem.value为输入,并且管sortByName是无状态/纯的. students是一个数组(引用).

    添加学生时,数组引用不会更改 - students不会更改 - 因此不会执行无状态/纯管道.

    当在过滤器输入中输入某些内容时,queryElem.value确实会更改,因此会执行无状态/纯管道.

    解决阵列问题的一种方法是每次添加学生时更改数组引用 - 即,每次添加学生时创建新数组.我们可以这样做concat():

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

    虽然这很有效,但我们的addNewStudent()方法不应该仅仅因为我们使用管道而以某种方式实现.我们想用来push()添加到我们的数组中.

    有状态的管道

    有状态管道具有状态 - 它们通常具有属性,而不仅仅是transform()方法.即使输入没有改变,也可能需要对它们进行评估.当我们指定管道是有状态/非纯的时 - pure: false- 每当Angular的更改检测系统检查组件的更改并且该组件使用有状态管道时,它将检查管道的输出,无论其输入是否已更改.

    这听起来像我们想要的,即使它效率较低,因为我们希望管道执行即使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的回答,"此错误仅在开发模式下发生(默认情况下从beta-0开始).如果enableProdMode()在引导应用程序时调用,则不会抛出错误." 国家文件ApplicationRef.tick():

    在开发模式中,tick()还执行第二个更改检测周期,以确保不会检测到进一步的更改.如果在第二个周期中拾取了其他更改,则应用中的绑定会产生无法在单个更改检测过程中解决的副作用.在这种情况下,Angular会抛出一个错误,因为Angular应用程序只能有一个更改检测通道,在此期间必须完成所有更改检测.

    在我们的场景中,我认为错误是虚假/误导.我们有一个有状态的管道,每次调用时输出都会改变 - 它可能有副作用,这没关系.NgFor在管道之后进行评估,因此它应该可以正常工作.

    但是,我们无法真正开发此错误,因此一种解决方法是将数组属性(即状态)添加到管道实现并始终返回该数组.请参阅@ pixelbits对此解决方案的回答.

    但是,我们可以更高效,并且正如我们将看到的,我们将不需要管道实现中的数组属性,并且我们不需要针对双重更改检测的变通方法.

    组件变化检测

    默认情况下,在每个浏览器事件中,角度变化检测都会遍历每个组件以查看它是否发生了变化 - 输入和模板(以及其他东西?)都会被检查.

    如果我们知道组件仅依赖于其输入属性(和模板事件),并且输入属性是不可变的,那么我们可以使用更有效的onPush变更检测策略.使用此策略,不会检查每个浏览器事件,只有在输入更改和模板事件触发时才会检查组件.而且,显然,我们没有Expression ... has changed after it was checked通过此设置获得该错误.这是因为在onPush再次"标记"(ChangeDetectorRef.markForCheck())之前不会再次检查组件.因此,模板绑定和有状态管道输出仅执行/评估一次.除非输入改变,否则无状态/纯管道仍未执行.所以我们仍然需要一个有状态的管道.

    这是@EricMartinez建议的解决方案:带有onPush变化检测的有状态管道.请参阅@ caffinatedmonkey对此解决方案的回答.

    请注意,使用此解决方案时,该transform()方法不需要每次都返回相同的数组.我发现有点奇怪:没有状态的有状态管道.再考虑一下......有状态管道可能应该总是返回相同的数组.否则,它只能与onPush开发模式下的组件一起使用.


    毕竟,我认为我喜欢@ Eric和@ pixelbits的答案的组合:有状态管道返回相同的数组引用,onPush如果组件允许则进行更改检测.由于有状态管道返回相同的数组引用,因此管道仍可用于未配置的组件onPush.

    Plunker

    这可能会成为Angular 2的习惯用法:如果数组正在输入管道,并且数组可能会更改(数组中的项目,而不是数组引用),我们需要使用有状态管道.



    1> Mark Rajcok..:

    为了完全理解问题和可能的解决方案,我们需要讨论角度变化检测 - 用于管道和组件.

    管道变化检测

    无状态/纯管

    默认情况下,管道是无状态/纯粹的.无状态/纯管道只是将输入数据转换为输出数据.他们不记得任何东西,所以他们没有任何属性 - 只是一种transform()方法.因此,Angular可以优化无状态/纯管道的处理:如果它们的输入没有改变,则在变化检测循环期间不需要执行管道.对于管道,如{{power | exponentialStrength: factor}},powerfactor输入.

    对于这个问题,"#student of students | sortByName:queryElem.value",studentsqueryElem.value为输入,并且管sortByName是无状态/纯的. students是一个数组(引用).

    添加学生时,数组引用不会更改 - students不会更改 - 因此不会执行无状态/纯管道.

    当在过滤器输入中输入某些内容时,queryElem.value确实会更改,因此会执行无状态/纯管道.

    解决阵列问题的一种方法是每次添加学生时更改数组引用 - 即,每次添加学生时创建新数组.我们可以这样做concat():

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

    虽然这很有效,但我们的addNewStudent()方法不应该仅仅因为我们使用管道而以某种方式实现.我们想用来push()添加到我们的数组中.

    有状态的管道

    有状态管道具有状态 - 它们通常具有属性,而不仅仅是transform()方法.即使输入没有改变,也可能需要对它们进行评估.当我们指定管道是有状态/非纯的时 - pure: false- 每当Angular的更改检测系统检查组件的更改并且该组件使用有状态管道时,它将检查管道的输出,无论其输入是否已更改.

    这听起来像我们想要的,即使它效率较低,因为我们希望管道执行即使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的回答,"此错误仅在开发模式下发生(默认情况下从beta-0开始).如果enableProdMode()在引导应用程序时调用,则不会抛出错误." 国家文件ApplicationRef.tick():

    在开发模式中,tick()还执行第二个更改检测周期,以确保不会检测到进一步的更改.如果在第二个周期中拾取了其他更改,则应用中的绑定会产生无法在单个更改检测过程中解决的副作用.在这种情况下,Angular会抛出一个错误,因为Angular应用程序只能有一个更改检测通道,在此期间必须完成所有更改检测.

    在我们的场景中,我认为错误是虚假/误导.我们有一个有状态的管道,每次调用时输出都会改变 - 它可能有副作用,这没关系.NgFor在管道之后进行评估,因此它应该可以正常工作.

    但是,我们无法真正开发此错误,因此一种解决方法是将数组属性(即状态)添加到管道实现并始终返回该数组.请参阅@ pixelbits对此解决方案的回答.

    但是,我们可以更高效,并且正如我们将看到的,我们将不需要管道实现中的数组属性,并且我们不需要针对双重更改检测的变通方法.

    组件变化检测

    默认情况下,在每个浏览器事件中,角度变化检测都会遍历每个组件以查看它是否发生了变化 - 输入和模板(以及其他东西?)都会被检查.

    如果我们知道组件仅依赖于其输入属性(和模板事件),并且输入属性是不可变的,那么我们可以使用更有效的onPush变更检测策略.使用此策略,不会检查每个浏览器事件,只有在输入更改和模板事件触发时才会检查组件.而且,显然,我们没有Expression ... has changed after it was checked通过此设置获得该错误.这是因为在onPush再次"标记"(ChangeDetectorRef.markForCheck())之前不会再次检查组件.因此,模板绑定和有状态管道输出仅执行/评估一次.除非输入改变,否则无状态/纯管道仍未执行.所以我们仍然需要一个有状态的管道.

    这是@EricMartinez建议的解决方案:带有onPush变化检测的有状态管道.请参阅@ caffinatedmonkey对此解决方案的回答.

    请注意,使用此解决方案时,该transform()方法不需要每次都返回相同的数组.我发现有点奇怪:没有状态的有状态管道.再考虑一下......有状态管道可能应该总是返回相同的数组.否则,它只能与onPush开发模式下的组件一起使用.


    毕竟,我认为我喜欢@ Eric和@ pixelbits的答案的组合:有状态管道返回相同的数组引用,onPush如果组件允许则进行更改检测.由于有状态管道返回相同的数组引用,因此管道仍可用于未配置的组件onPush.

    Plunker

    这可能会成为Angular 2的习惯用法:如果数组正在输入管道,并且数组可能会更改(数组中的项目,而不是数组引用),我们需要使用有状态管道.



    2> 0xcaff..:

    正如埃里克·马丁内斯在评论中指出,添加pure: false到您的Pipe装饰,并changeDetection: ChangeDetectionStrategy.OnPush以你的Component装饰将解决您的问题.这是一个工作的plunkr.改变ChangeDetectionStrategy.Always,也有效.这就是原因.

    根据管道上的angular2指南:

    管道默认是无状态的.我们必须通过设置装饰器的pure属性来声明管道是有状态的.此设置告诉Angular的变化检测系统在每个循环中检查该管道的输出,无论其输入是否已更改.@Pipefalse

    至于ChangeDetectionStrategy默认情况下,每个循环都会检查所有绑定.当pure: false添加管道,我相信改变检测方法的变化,从CheckAlwaysCheckOnce性能方面的原因.使用OnPush,只有在输入属性更改或触发事件时才会检查Component的绑定.有关变化探测器的更多信息angular2,请参阅以下链接:

    角度变化检测2

    https://github.com/angular/angular/issues/4746

    变化检测重新发明Victor Savkin



    3> pixelbits..:

    演示Plunkr

    您无需更改ChangeDetectionStrategy.实现有状态管道足以让一切正常运行.

    这是一个有状态的管道(没有进行其他更改):

    @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;
      }
    }
    



    4> 小智..:

    从角度文档

    纯净和不纯的管道

    有两类管道:纯净和不纯.管道默认是纯净的.到目前为止,你见过的每根烟斗都是纯净的.通过将其纯标志设置为false来使管道不纯.你可以让FlyingHeroesPipe不纯洁:

    @Pipe({ name: 'flyingHeroesImpure', pure: false })

    在此之前,了解纯净和不纯的区别,从纯管道开始.

    纯管道Angular仅在检测到输入值的纯变化时才执行纯管道.纯变化是对原始输入值(String,Number,Boolean,Symbol)的更改或更改的对象引用(Date,Array,Function,Object).

    Angular忽略(复合)对象内的更改.如果更改输入月份,添加到输入数组或更新输入对象属性,它将不会调用纯管道.

    这似乎有限制但它也很快.对象引用检查比深度检查差异要快得多 - 因此Angular可以快速确定它是否可以跳过管道执行和视图更新.

    因此,当您可以使用变化检测策略时,最好使用纯管道.如果不能,可以使用不纯的管道.

    推荐阅读
    虎仔球妈_459
    这个屌丝很懒,什么也没留下!
    DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
    Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有