2016-01-14 27 views
50

私はこれについて最初に質問するのではないことを知っていますが、これまでの質問では答えが見つかりません。私はこれを1つのコンポーネントで持っていますAngular2変化の検出:ngOnChangesがネストされたオブジェクトに対して発砲しない

コントローラでは、rawLapsdataは時々突然変更されます。

lapsには、データが表形式でHTMLとして出力されます。 rawLapsdataが変更されるたびに変更されます。

私のmapコンポーネントは、ngOnChangesをGoogleマップのマーカーを再描画するためのトリガーとして使用する必要があります。問題は、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); 

とデータはあなたがzone.run(...)内のデータupateステートメントを実行する必要があります外部ライブラリから来る場合this.rawLapsDataが大きいJSONオブジェクト

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

コードには、データの更新方法やデータの種類は表示されません。新しいインスタンスが割り当てられているか、値のプロパティに変更されていますか? –

+0

@GünterZöchbauer親コンポーネントから行を追加しました –

+0

'zone.run(...) 'にこの行をラップすると思います。 –

答えて

63

rawLapsDataは、配列の内容を変更しても(アイテムの追加、アイテムの削除、アイテムの変更など)、引き続き同じ配列を参照し続けます。

変更検出時に、Angularがコンポーネントの入力プロパティの変更をチェックすると、ダーティチェックに(本質的に)===が使用されます。配列の場合、これは配列参照(のみ)がダーティチェックされていることを意味します。 rawLapsData配列参照が変更されていないため、ngOnChanges()は呼び出されません。私は2つの可能な解決策の

を考えることができます:

  1. ngDoCheck()を実装し、配列の内容が変更されているかどうかを判断するために、独自の変化検出ロジックを実行します。 (ライフサイクルフックの文書はan exampleです。)

  2. アレイの内容を変更するたびに、rawLapsDataに新しいアレイを割り当てます。配列(参照)が変更として表示されるため、ngOnChanges()が呼び出されます。

あなたの答えでは、別の解決策が思い浮かびました。

OPにここにいくつかのコメントを繰り返す:

私はまだlapsが変更に拾うことができますどのように表示されていないmapはできませんが(確かにそれはngOnChanges()自身と同等のものを使用する必要があります?) 。 laps成分で

  • コード/テンプレートがlapsDataアレイ内の各エントリをループ、およびコンテンツを表示するので、表示されているデータの各部分に角度バインディングがあります。
  • 角度は、コンポーネントの入力プロパティの変更を検出しないとしても
  • 、それはまだテンプレートバインディングのすべてのダーティチェック(デフォルト)( ===チェックを使用して)。これらのいずれかが変更されると、AngularはDOMを更新します。それはあなたが見ているものです。
  • mapsコンポーネントのテンプレートに入力プロパティのlapsDataのバインディングが存在しない可能性があります。それは違いを説明するでしょう。

親コンポーネントのと親コンポーネントのrawLapsDataは、すべて同じ/ 1つの配列を指していることに注意してください。したがって、AngularはlapsData入力プロパティの変更(参照)に気づいていませんが、コンポーネントはすべて1つの配列を共有/参照しているため、配列の内容が変化したり参照したりします。プリミティブ型(string、number、boolean)のように、Angularがこれらの変更を伝播する必要はありません。しかし、プリミティブ型では、値を変更すると常に答え/ソリューションで悪用するものがngOnChanges() –になります。

オブジェクトの入力プロパティは、配列入力プロパティと同じ動作をしている可能性があります。

+0

はい、深くネストされたオブジェクトを変更していましたが、 Angularが構造の変化を見つけるのが難しくなったと思います。私の好みではありませんが、これはXMLに変換されています。最後にXMLを再作成したいので、周囲のデータを失う余裕はありません。 –

+5

@SimonH、 "Angularが構造の変化を見つけるのは難しい" - - ちょうど分かりやすくするために、Angularは入力プロパティ内の配列やオブジェクトを変更のために調べていません。値が変更されたかどうかを調べるだけです。オブジェクトと配列の場合、値は参照値です。プリミティブ型の場合、値は...値です。 (私はすべての用語をまっすぐに持っているかどうかはわかりませんが、あなたはそのアイデアを得ます) –

+9

素晴らしい答え。 Angular2チームは、変更検出の内部についての詳細で信頼できる文書を必然的に公開する必要があります。 –

4

の真ん中へのポインタそのものであることに注意してください。これにはzone: NgZoneを注入します。外部ライブラリのインスタンス化をzone.run()内ですでに実行できる場合は、後でzone.run()を必要としない可能性があります。

+0

OPへのコメントに記されているように、変更は外部ではなくjsonオブジェクトの深いところにあります –

+1

あなたの答えによれば、angle1のようにAngular2で同期を維持するために何かを実行する必要があります。適用? –

+1

Angularの外側から何かが開始された場合、ゾーンによってパッチされたAPIは使用されず、Angularは変更の可能性について通知されません。はい、 'zone.run'は' $ scope.apply'に似ています。 –

3

私の「ハック」ソリューションはrawLapsDataと同時に

<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の変化であり、それは単純 オブジェクト プリミティブ型による変化を検出する別の機会をマッピングできます。エレガントではありませんが、機能します。

+0

テンプレート構文、特に中規模/大規模アプリケーションのさまざまなコンポーネントのすべての変更を追跡するのは難しいです。通常、私は共有イベントエミッタとサブスクリプションを使用してデータ(クイックソリューション)を渡すか、これに対してRx.Subjectを介してReduxパターンを実装します... – Sasxa

3

ここでは、ちょうどこの問題で私を困惑させたハックです。

OPと同様のシナリオでは、データを渡す必要があるネストされたAngularコンポーネントがありますが、入力が配列を指し示しています。前述のように、Angularでは変更が表示されません配列の内容は調べません。

これを修正するために、配列をAngularの文字列に変換して変更を検出し、次に入れ子になったコンポーネントで配列を再配置して幸せな日に戻します。

+0

Yuck! array.slice(0)アプローチははるかにクリーンです。 –

3

使用ChangeDetectorRef.detectChanges()あなたがネストされたオブジェクトを(それはそのダーティチェックでミスという)を編集したときに、変化検出を実行するために角度指示します。

7

ないクリーンなアプローチ、あなただけのオブジェクトに値を変更するたびに複製することができますか?

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

私は、@GünterZöchbauerようなあなたngDoCheck()自身が、おそらく誰かがチャイムができ実装する上で、このアプローチを好むだろうと思います。

+0

ターゲットブラウザがをサポートするかどうか不明な場合は、json文字列に文字列化し、jsonに解析して新しいオブジェクトを作成することもできます。 – Guntram

3

マークRajcokの第二の溶液に延長

として新しいの割り当て配列の内容を に変更するたびに、配列をrawLapsDataに設定します。

rawLapsData = rawLapsData.slice(0); 

ため

私はこれを言及しています:配列 (参照)が変更

としてあなたは、このような配列の内容のクローンを作成することができます表示されますので、その後ngOnChanges()が呼び出されます

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

は私にとっては機能しませんでした。私はこれが役立つことを願っています

関連する問題