Angular2更改检测:ngOnChanges不为嵌套对象触发

Angular2更改检测:ngOnChanges不为嵌套对象触发,angular,Angular,我知道我不是第一个问这个问题的人,但我在前面的问题中找不到答案。我有一个组件 <div class="col-sm-5"> <laps [lapsData]="rawLapsData" [selectedTps]="selectedTps" (lapsHandler)="lapsHandler($event)"> </laps> </div> <map [lapsDat

我知道我不是第一个问这个问题的人,但我在前面的问题中找不到答案。我有一个组件

<div class="col-sm-5">
    <laps
        [lapsData]="rawLapsData"
        [selectedTps]="selectedTps"
        (lapsHandler)="lapsHandler($event)">
    </laps>
</div>

<map
    [lapsData]="rawLapsData"
    class="col-sm-7">
</map>
更新:ngOnChanges不起作用,但看起来lapsData正在更新。在ngInit中,是一个用于缩放更改的事件侦听器,它还调用
this.drawmarkers
。当我改变缩放时,我确实看到了标记的变化。所以唯一的问题是,在输入数据更改时,我没有收到通知

在家长,我有这一行。(回想一下,变化反映在圈数中,但不反映在地图中)

请注意,
this.rawLapsData
本身就是一个指向大型json对象中间的指针

this.rawLapsData = this.main.data.TrainingCenterDatabase.Activities[0].Activity[0].Lap;

如果数据来自外部库,则可能需要在
区域内运行data upate语句。运行(…)
。为此注入
zone:NgZone
。如果您已经可以在
zone.run()
中运行外部库的实例化,那么您以后可能不需要
zone.run()

我的“黑客”解决方案是

   <div class="col-sm-5">
        <laps
            [lapsData]="rawLapsData"
            [selectedTps]="selectedTps"
            (lapsHandler)="lapsHandler($event)">
        </laps>
    </div>
    <map
        [lapsData]="rawLapsData"
        [selectedTps]="selectedTps"   // <--------
        class="col-sm-7">
    </map>


rawLapsData
继续指向同一数组,即使您修改了数组的内容(例如,添加项、删除项、更改项)

在更改检测期间,当Angular检查组件的输入属性以进行更改时,它使用(基本上)脏检查。对于数组,这意味着数组引用(仅)是脏检查的。由于
rawLapsData
数组引用没有更改,因此不会调用
ngOnChanges()

我可以想出两种可能的解决办法:

  • 实现
    ngDoCheck()
    并执行您自己的更改检测逻辑,以确定数组内容是否已更改。(生命周期钩子文档具有。)

  • 每当对数组内容进行任何更改时,将新数组分配给
    rawLapsData
    。然后将调用
    ngOnChanges()
    ,因为数组(引用)将显示为更改

  • 在你的回答中,你提出了另一个解决方案

    在此重复关于OP的一些评论:

    我仍然不明白
    laps
    是如何接受变化的(当然它必须使用与
    ngochanges()
    本身相同的东西?),而
    map
    却不能

    • laps
      组件中,您的代码/模板在
      lapsData
      数组中的每个条目上循环,并显示内容,因此显示的每个数据段上都有角度绑定
    • 即使Angular没有检测到组件输入属性的任何更改(使用
      =
      检查),它仍然(默认情况下)脏检查所有模板绑定。当其中任何更改时,Angular将更新DOM。这就是你所看到的
    • 映射
      组件的模板中可能没有任何绑定到其
      lapsData
      输入属性,对吗?这就解释了区别
    请注意,两个组件中的
    lapsData
    和父组件中的
    rawLapsData
    都指向同一个数组。因此,即使Angular没有注意到对
    lapsData
    输入属性的任何(引用)更改,组件“get”/也会看到任何数组内容的更改,因为它们都共享/引用该数组。我们不需要像对待基本类型(字符串、数字、布尔值)那样传播这些更改。但是对于基元类型,对值的任何更改都会触发
    ngochanges()
    ——这是您在答案/解决方案中利用的


    正如您现在可能已经了解到的,对象输入属性与数组输入属性具有相同的行为。

    这里有一个让我从这个问题中解脱出来的方法

    因此类似于OP的场景-我有一个嵌套的Angular组件,需要将数据传递给它,但是输入点指向一个数组,如上所述,Angular没有看到变化,因为它没有检查数组的内容


    因此,为了修复它,我将数组转换为Angular的字符串以检测更改,然后在嵌套组件中,我将(“,”)字符串拆分回一个数组及其快乐日子。

    使用
    changedethetectorref.detectChanges()
    告诉Angular在编辑嵌套对象时运行更改检测(它未通过脏检查).

    不是最干净的方法,但每次更改值时都可以克隆对象

       rawLapsData = Object.assign({}, rawLapsData);
    

    我想我更喜欢这种方法,而不是实现您自己的
    ngDoCheck()
    ,但也许像@GünterZöchbauer这样的人可以加入进来。

    作为Mark Rajcok第二个解决方案的扩展

    每当对数组进行任何更改时,将新数组分配给rawLapsData 数组内容。然后将调用ngOnChanges(),因为数组 (参考)将显示为更改

    可以按如下方式克隆阵列的内容:

    rawLapsData = rawLapsData.slice(0);
    
    rawLapsData = somevalue; // change detection will not happen
    
    我提到这一点是因为

    rawLapsData=Object.assign({},rawLapsData)


    不适合我。我希望这会有所帮助。

    当您更改对象(包括嵌套对象)的属性时,不会触发更改检测。一种解决方案是使用“lodash”clone()函数重新分配新的对象引用

    import * as _ from 'lodash';
    
    this.foo = _.clone(this.foo);
    

    对于阵列,您可以这样做:

    rawLapsData = rawLapsData.slice(0);
    
    rawLapsData = somevalue; // change detection will not happen
    
    在更新
    rawLapsData的
    .ts
    文件(父组件)中,执行以下操作:

    rawLapsData = rawLapsData.slice(0);
    
    rawLapsData = somevalue; // change detection will not happen
    

    解决方案:

    rawLapsData = {...somevalue}; //change detection will happen
    

    ngOnChanges
    将在子组件中调用我偶然发现了同样的需求。我在这方面读了很多书,这是我关于这个问题的看法

    <div class="col-sm-5">
        <laps
            [lapsData]="rawLapsData"
            [selectedTps]="selectedTps"
            (lapsHandler)="lapsHandler($event)">
        </laps>
    </div>
    
    <map
        [lapsData]="rawLapsData"
        class="col-sm-7">
    </map>
    
    如果您希望在推送时进行更改检测,那么当您更改右侧对象的值时,您会有它吗?如果你以某种方式移除物体,你也会得到它

    如前所述,使用changeDetectionStrategy.onPush

    myCollection[5].attribute = 'new value'; refresh();

    refresh() : void {
        this.myCollection = this.myCollection.slice();
    }
    
    this.arrayWeNeed.DoWhatWeNeedWithThisArray();
    const tempArray = [...arrayWeNeed];
    this.arrayWeNeed = [];
    this.arrayWeNeed = tempArray;
    
    I created a Boolean Input variable and toggled it whenever array changed, which triggered change detection in the child component, hence achieving the purpose
    
     this.pagingObj = new Paging(); //This line did the magic
     this.pagingObj.pageNumber = response.PageNumber;
    
    this.data.profiles[i].icon.url = '';
    
    let array = this.data.profiles.map(x => Object.assign({}, x)); // It will detect changes
    
    ngOnChanges(changes: SimpleChanges) { 
        if (changes.childObj) {// your logic here}
    }