[angular] 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, 데이터는 표 형식으로 HTML로서 출력된다. 변경 될 때마다 rawLapsdata변경됩니다.

map구성 요소의 요구에 사용하는 ngOnChanges구글지도에 마커를 다시 그리기 트리거로. 문제는 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 . 줌을 변경하면 실제로 마커가 변경되는 것을 볼 수 있습니다. 따라서 유일한 문제는 입력 데이터가 변경 될 때 알림을받지 못한다는 것입니다.

부모님에게는이 줄이 있습니다. 변경 사항은 랩에는 반영되지만 맵에는 반영되지 않습니다.

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()배열 내용이 변경되었는지 확인하기 위해 자체 변경 감지 논리를 구현 하고 수행하십시오. (Lifecycle Hooks 문서에는 예제가 있습니다.)

  2. rawLapsData배열 내용을 변경할 때마다 새 배열을 할당하십시오 . 그러면 ngOnChanges()배열 (참조)이 변경으로 표시되므로 호출됩니다.

당신의 대답에서, 당신은 다른 해결책을 생각해 냈습니다.

OP에 대한 의견을 반복합니다.

여전히 laps변경 사항을 가져올 수 있는 방법을 보지 못했습니다 (확실히ngOnChanges() 자신 합니까?) map.

  • 에서 laps구성 요소 코드 / 된 템플릿의 각 항목을 통해 루프 lapsData배열 및 표시 내용 때문에 표시되는 데이터의 각 부분에 각도 바인딩이있다.
  • Angular가 구성 요소의 입력 속성에 대한 변경 사항을 감지하지 않더라도 ( ===확인 사용 ) 여전히 기본적으로 모든 템플릿 바인딩을 더티 확인합니다. 이 중 하나라도 변경되면 Angular는 DOM을 업데이트합니다. 그것이 당신이보고있는 것입니다.
  • maps구성 요소는 가능성의에 해당 템플리트의 모든 바인딩이없는 lapsData입력 속성을, 맞죠? 그것은 차이점을 설명 할 것입니다.

참고로 lapsData두 구성 요소와 rawLapsData동일한 / 하나의 어레이에 대한 모든 포인트 부모 구성 요소이다. 따라서 Angular가 lapsData입력 속성에 대한 (참조) 변경 사항을 인식하지 않더라도 구성 요소는 하나의 배열을 공유 / 참조하기 때문에 “get”/ 배열 내용 변경을 볼 수 있습니다. 기본 유형 (문자열, 숫자, 부울)에서와 같이 이러한 변경 사항을 전파하기 위해 Angular가 필요하지 않습니다. 그러나 기본 유형의 경우 값을 변경하면 항상 트리거됩니다 ngOnChanges(). 이는 답변 / 솔루션에서 악용하는 것입니다.

아마 지금까지 알아 낸 것처럼 객체 입력 속성은 배열 입력 속성과 동일한 동작을 갖습니다.


답변

가장 깨끗한 접근 방식은 아니지만 값을 변경할 때마다 객체를 복제 할 수 있습니까?

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

나는 당신 자신을 구현하는 것 ngDoCheck()보다이 접근법을 선호 하지만 @ GünterZöchbauer와 같은 누군가가 차임 할 수 있다고 생각합니다 .


답변

배열의 경우 다음과 같이 할 수 있습니다.

에서 .ts파일 (부모 요소) 여기서 당신은 당신을 업데이트 rawLapsData이런 식으로 작업을 수행합니다

rawLapsData = somevalue; // change detection will not happen

해결책:

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

ngOnChanges하위 구성 요소에서 호출됩니다


답변

Mark Rajcok의 두 번째 솔루션 확장

배열 내용을 변경할 때마다 rawLapsData에 새 배열을 할당하십시오. 그러면 배열 (참조)이 변경으로 표시되므로 ngOnChanges ()가 호출됩니다.

다음과 같이 배열의 내용을 복제 할 수 있습니다.

rawLapsData = rawLapsData.slice(0);

나는 이것을 언급하고 있기 때문에

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

나를 위해 일하지 않았다. 이게 도움이 되길 바란다.


답변

데이터가 외부 라이브러리에서 온 경우 데이터 업데이트 명령문을에서 실행해야 할 수도 있습니다 zone.run(...). zone: NgZone이것을 주입 하십시오. zone.run()이미 외부 라이브러리의 인스턴스화를 실행할 수 있으면 zone.run()나중에 필요하지 않을 수 있습니다 .


답변

사용은 ChangeDetectorRef.detectChanges()당신이 (그것의 더러운 검사로 그리워 있음) 중첩 된 개체를 편집 할 때 변경 감지를 실행하는 각도를 알 수 있습니다.


답변

문제를 해결하기위한 2 가지 해결책이 있습니다

  1. 데이터 변경 여부 ngDoCheck를 감지하는 object데 사용
  2. 상위 구성 요소에서 object새 메모리 주소를 지정하십시오 object = Object.create(object).