Angular2 更改检测:ngOnChanges 不会为嵌套对象触发 [英] Angular2 change detection: ngOnChanges not firing for nested object

查看:34
本文介绍了Angular2 更改检测:ngOnChanges 不会为嵌套对象触发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

<圈数[圈数数据]=原始圈数数据"[selectedTps]=selectedTps";(lapsHandler)=lapsHandler($event)"></圈>

<地图[圈数数据]=原始圈数数据"class=col-sm-7"></地图>

在控制器中,rawLapsdata 会不时发生变化.

laps中,数据以表格格式输出为HTML.每当 rawLapsdata 更改时,它都会更改.

我的map 组件需要使用ngOnChanges 作为触发器来重绘谷歌地图上的标记.问题是 ngOnChanges 在父级中的 rawLapsData 更改时不会触发.我能做什么?

import {Component, Input, OnInit, OnChanges, SimpleChange} from 'angular2/core';@成分({选择器:'地图',templateUrl: './components/edMap/edMap.html',styleUrls: ['./components/edMap/edMap.css']})导出类 MapCmp 实现 OnInit、OnChanges {@Input() 圈数数据:任何;地图:google.maps.Map;ngOnInit() {...}ngOnChanges(changes: { [propName: string]: SimpleChange }) {console.log('ngOnChanges = ', changes['lapsData']);if (this.map) this.drawMarkers();}


更新: ngOnChanges 不起作用,但看起来 lapsData 正在更新.在 ngOnInit 中是缩放更改的事件侦听器,它也调用 this.drawmarkers.当我更改缩放比例时,我确实看到标记发生了变化.所以唯一的问题是我在输入数据更改时没有收到通知.

在父级中,我有这条线.(回想一下,变化反映在圈数中,而不是在 map 中).

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

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

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

解决方案

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

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

我能想到两种可能的解决方案:

  1. 实施 ngDoCheck() 并执行您自己的更改检测逻辑以确定数组内容是否已更改.(生命周期钩子文档有一个例子.)

  2. 每当您对数组内容进行任何更改时,都会为 rawLapsData 分配一个新数组.然后 ngOnChanges() 将被调用,因为数组(引用)将显示为更改.

在您的回答中,您提出了另一种解决方案.

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

<块引用>

我仍然不明白 laps 如何接受更改(肯定它必须使用与 ngOnChanges() 本身等效的东西?)而 地图不能.

  • laps 组件中,您的代码/模板循环遍历 lapsData 数组中的每个条目,并显示内容,因此每个数据块上都有 Angular 绑定显示.
  • 即使 Angular 没有检测到组件输入属性的任何更改(使用 === 检查),它仍然(默认情况下)脏检查所有模板绑定.当其中任何一个发生变化时,Angular 将更新 DOM.这就是你所看到的.
  • maps 组件的模板中可能没有任何绑定到它的 lapsData 输入属性,对吧?这可以解释差异.

请注意,两个组件中的 lapsData 和父组件中的 rawLapsData 都指向同一个/一个数组.因此,即使 Angular 没有注意到 lapsData 输入属性的任何(引用)更改,组件也会获取"/查看任何数组内容的更改,因为它们都共享/引用该数组.我们不需要 Angular 来传播这些更改,就像我们使用原始类型(字符串、数字、布尔值)一样.但是对于原始类型,对值的任何更改都会触发 ngOnChanges() –这是您在答案/解决方案中利用的东西.

正如您现在可能已经发现的那样,对象输入属性与数组输入属性具有相同的行为.

I know I am not the first to ask about this, but I can't find an answer in the previous questions. I have this in one component

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

<map
    [lapsData]="rawLapsData"
    class="col-sm-7">
</map>

In the controller rawLapsdata gets mutated from time to time.

In laps, the data is output as HTML in a tabular format. This changes whenever rawLapsdata changes.

My map component needs to use ngOnChanges as a trigger to redraw markers on a Google Map. The problem is that ngOnChanges does not fire when rawLapsData changes in the parent. What can I do?

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


Update: ngOnChanges is not working, but it looks as though lapsData is being updated. In the ngOnInit is an event listener for zoom changes that also calls this.drawmarkers. When I change the zoom I do indeed see a change in markers. So the only issue is that I don't get the notification at the time the input data changes.

In the parent, I have this line. (Recall that the change is reflected in laps, but not in map).

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

And note that this.rawLapsData is itself a pointer to the middle of a large json object

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

解决方案

rawLapsData continues to point to the same array, even if you modify the contents of the array (e.g., add items, remove items, change an item).

During change detection, when Angular checks components' input properties for change, it uses (essentially) === for dirty checking. For arrays, this means the array references (only) are dirty checked. Since the rawLapsData array reference isn't changing, ngOnChanges() will not be called.

I can think of two possible solutions:

  1. Implement ngDoCheck() and perform your own change detection logic to determine if the array contents have changed. (The Lifecycle Hooks doc has an example.)

  2. Assign a new array to rawLapsData whenever you make any changes to the array contents. Then ngOnChanges() will be called because the array (reference) will appear as a change.

In your answer, you came up with another solution.

Repeating some comments here on the OP:

I still don't see how laps can pick up on the change (surely it must be using something equivalent to ngOnChanges() itself?) while map can't.

  • In the laps component your code/template loops over each entry in the lapsData array, and displays the contents, so there are Angular bindings on each piece of data that is displayed.
  • Even when Angular doesn't detect any changes to a component's input properties (using === checking), it still (by default) dirty checks all of the template bindings. When any of those change, Angular will update the DOM. That's what you are seeing.
  • The maps component likely doesn't have any bindings in its template to its lapsData input property, right? That would explain the difference.

Note that lapsData in both components and rawLapsData in the parent component all point to the same/one array. So even though Angular doesn't notice any (reference) changes to the lapsData input properties, the components "get"/see any array contents changes because they all share/reference that one array. We don't need Angular to propagate these changes, like we would with a primitive type (string, number, boolean). But with a primitive type, any change to the value would always trigger ngOnChanges() – which is something you exploit in your answer/solution.

As you probably figured out by now object input properties have the same behavior as array input properties.

这篇关于Angular2 更改检测:ngOnChanges 不会为嵌套对象触发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆