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

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

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

在控制器中, rawLapsdata会不时发生变异。

laps ,数据以表格forms作为HTML输出。 每当rawLapsdata改变时, rawLapsdata改变。

我的map组件需要使用ngOnChanges作为触发器来重绘Google Map上的标记。 问题是,在父rawLapsData更改rawLapsData时,ngOnChanges不会触发。 我能做什么?

 import {Component, Input, OnInit, OnChanges, SimpleChange} from 'angular2/core'; @Component({ selector: 'map', templateUrl: './components/edMap/edMap.html', styleUrls: ['./components/edMap/edMap.css'] }) export class MapCmp implements OnInit, OnChanges { @Input() lapsData: any; map: google.maps.Map; ngOnInit() { ... } ngOnChanges(changes: { [propName: string]: SimpleChange }) { console.log('ngOnChanges = ', changes['lapsData']); if (this.map) this.drawMarkers(); } 

更新: ngOnChanges不工作,但它看起来好像lapsData正在更新。 在ngInit是一个缩放更改的事件监听器,也调用this.drawmarkers 。 当我改变缩放,我确实看到标记的变化。 所以唯一的问题是我在input数据改变的时候没有得到通知。

在父母,我有这条线。 (回想一下,变化反映在圈,但不是在地图上)。

 this.rawLapsData = deletePoints(this.rawLapsData, this.selectedTps); 

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

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

即使您修改数组的内容(例如,添加项目,移除项目,更改项目), rawLapsData继续指向相同的数组。

在更改检测期间,当Angular检查组件的input属性以进行更改时,它使用(本质上) ===进行脏检查。 对于数组来说,这意味着数组引用(仅)被脏检查。 由于rawLapsData数组引用不会更改, ngOnChanges()不会调用ngOnChanges()

我可以想到两个可能的解决scheme:

  1. 实现ngDoCheck()并执行你自己的变化检测逻辑来确定数组内容是否已经改变。 (Lifecycle Hooks文档有一个例子 。)

  2. 无论何时对数组内容进行任何更改,都可以将新数组分配给rawLapsData 。 然后ngOnChanges()将被调用,因为数组(参考)将显示为一个更改。

在你的回答中,你提出了另一个解决scheme。

在OP上重复一些评论:

我还没有看到如何laps可以采取的变化(当然,它必须使用相当于ngOnChanges()本身?),而map不能。

  • laps组件中,你的代码/模板在lapsData数组中的每个条目上循环,并显示内容,所以在显示的每一条数据上都有Angular绑定。
  • 即使Angular没有检测到组件input属性的任何改变(使用===检查),它仍然(默认情况下)脏检查所有的模板绑定。 当这些变化中的任何一个,Angular会更新DOM。 这就是你所看到的。
  • maps组件可能在其模板中没有绑定到lapsDatainput属性,对吧? 这将解释不同之处。

请注意,组件中的rawLapsData和父组件中的rawLapsData都指向相同的/一个数组。 所以即使Angular没有注意到lapsDatainput属性的任何(引用)变化,组件“获取”/看到任何数组内容的变化,因为他们都共享/引用一个数组。 我们不需要Angular来传播这些变化,就像我们用原始types(string,number,boolean)一样。 但是对于一个原始types,任何对该值的改变总是会触发ngOnChanges() – 这是你在你的答案/解决scheme中利用的东西。

正如你现在可能已经发现对象input属性具有与数组input属性相同的行为。

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

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

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

我想我会更喜欢这种方法来实现你自己的ngDoCheck()但也许像@GünterZöchbauer可能会有人参加。

我的'黑客'解决scheme是

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

selectedTps与rawLapsData同时发生更改,并使映射有更多机会通过更简单的对象基元types来检测更改。 这不是优雅,但它的作品。

这是一个黑客,让我摆脱这个麻烦。

因此,类似的情况下的OP – 我有一个嵌套的Angular组件需要数据传递给它,但input指向一个数组,如上所述,Angular没有看到一个变化,因为它不检查数组的内容。

所以要解决这个问题,我把数组转换为Angular的string来检测一个变化,然后在嵌套的组件中,我把string分割(',')到一个数组,然后又重新开始。

使用ChangeDetectorRef.detectChanges()来告诉Angular在编辑一个嵌套的对象时(它错过了脏的检查)运行一个更改检测。

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

无论何时对数组内容进行任何更改,都可以将新数组分配给rawLapsData。 然后ngOnChanges()将被调用,因为数组(引用)将作为一个改变出现

你可以像这样克隆数组的内容:

 rawLapsData = rawLapsData.slice(0); 

我提到这是因为

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

没有为我工作。 我希望这有帮助。