Angular2 wykrywanie zmian: ngOnChanges nie uruchamia się dla zagnieżdżonego obiektu

Wiem, że nie jestem pierwszym, który o to pyta, ale nie mogę znaleźć odpowiedzi w poprzednich pytaniach. Mam to w jednym komponencie

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

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

W kontrolerze rawLapsdata od czasu do czasu ulega mutacji.

W laps Dane są wyświetlane w formacie HTML w formacie tabelarycznym. To zmienia się za każdym razem, gdy rawLapsdata się zmienia.

Mój komponent map musi użyć ngOnChanges jako wyzwalacza do przerysowania znaczników na mapie Google. Problem polega na tym, że ngOnChanges nie odpala się, gdy rawLapsData zmienia się w rodzic. Co mogę zrobić?

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 nie działa, ale wygląda na to, że lapsData jest aktualizowana. W ngInit jest detektorem zdarzeń dla zmian powiększenia, który również wywołuje this.drawmarkers. Kiedy zmieniam zoom, rzeczywiście widzę zmianę znaczników. Więc jedynym problemem jest to, że nie dostaję powiadomienia w momencie zmiany danych wejściowych.

W rodzicu, mam ten wiersz. (Przypomnijmy, że zmiana znajduje odzwierciedlenie w okrążeniach, ale nie w Mapa).

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

I zauważ, że this.rawLapsData jest sam w sobie wskaźnikiem do środka dużego obiektu json

this.rawLapsData = this.main.data.TrainingCenterDatabase.Activities[0].Activity[0].Lap;
 70
Author: mortalis, 2016-01-14

10 answers

rawLapsData kontynuuje wskazywanie na tę samą tablicę, nawet jeśli zmodyfikujesz zawartość tablicy (np. Dodaj elementy, Usuń elementy, Zmień element).

Podczas wykrywania zmian, gdy kątowe sprawdza właściwości wejściowe komponentów pod kątem zmian, używa (zasadniczo) === do sprawdzania brudnych. W przypadku tablic oznacza to, że odwołania do tablicy (tylko) są brudne sprawdzane. Ponieważ odniesienie do tablicy rawLapsData nie ulega zmianie, ngOnChanges() nie zostanie wywołane.

Myślę o dwóch możliwych rozwiązania:

  1. Zaimplementuj ngDoCheck() i wykonaj własną logikę wykrywania zmian, aby określić, czy zawartość tablicy uległa zmianie. (Dokument Lifecycle Hooks zawiera przykład.)

  2. Przypisz nową tablicę do rawLapsData za każdym razem, gdy wprowadzasz jakiekolwiek zmiany w zawartości tablicy. Następnie zostanie wywołana ngOnChanges(), ponieważ tablica (Referencja) pojawi się jako zmiana.

W swojej odpowiedzi, wymyśliłeś inne rozwiązanie.

Powtarzanie niektórych komentarze tutaj na temat OP:

Nadal nie widzę, jak laps może odebrać zmianę (na pewno musi ona używać czegoś równoważnego samej ngOnChanges()?) while map can ' t.

  • w składniku laps twój kod/szablon pętli nad każdym wpisem w tablicy lapsData i wyświetla zawartość, więc są wiązania kątowe na każdym wyświetlanym fragmencie danych.
  • nawet wtedy, gdy Angular nie wykrywa żadnych zmian we właściwościach wejściowych komponentu (używając === sprawdzanie), nadal (domyślnie) sprawdza wszystkie powiązania szablonu. Gdy którakolwiek z tych zmian, Angular zaktualizuje DOM. To właśnie widzisz.
  • komponent maps prawdopodobnie nie ma żadnych powiązań w szablonie do swojej właściwości wejściowej lapsData, prawda? To by wyjaśniało różnicę.

Zauważ, że lapsData w obu komponentach i rawLapsData w komponencie nadrzędnym wszystkie wskazują na tę samą / jedną tablicę. Więc mimo, że Angular nie zauważa żadnych (referencyjnych) zmian do właściwości wejściowych lapsData komponenty " get " / see any array contents changes, ponieważ wszystkie dzielą / odwołują się do tej jednej tablicy. Nie potrzebujemy Angular, aby propagować te zmiany, jak w przypadku prymitywnego typu (string, number, boolean). Ale w przypadku prymitywnego typu, każda zmiana wartości zawsze wyzwalałaby ngOnChanges() - co jest czymś, co wykorzystujesz w swojej odpowiedzi/rozwiązaniu.

Jak już zapewne zauważyłeś właściwości wejścia obiektu mają takie samo zachowanie jak wejście tablicy właściwości.

 85
Author: Mark Rajcok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-07 15:52:49

Nie najczystsze podejście, ale możesz po prostu sklonować obiekt za każdym razem, gdy zmienisz wartość?

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

Myślę, że wolałbym takie podejście niż implementację własnego ngDoCheck(), ale może ktoś taki jak @günterzöchbauer mógłby to zrobić.

 13
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-03 15:18:59

Jako rozszerzenie drugiego rozwiązania Marka Rajcoka

Przypisz nową tablicę do rawLapsData za każdym razem, gdy wprowadzasz jakiekolwiek zmiany w zawartość tablicy. Wtedy ngOnChanges () zostanie wywołana, ponieważ tablica (reference) pojawi się jako zmiana

Możesz sklonować zawartość tablicy w następujący sposób:

rawLapsData = rawLapsData.slice(0);

Wspominam o tym, ponieważ

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

Nie zadziałało na mnie. Mam nadzieję, że to pomoże.
 8
Author: decebal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-29 15:12:20

Jeśli dane pochodzą z zewnętrznej biblioteki, może być konieczne uruchomienie instrukcji data upate w zone.run(...). Wstrzyknąć zone: NgZone w tym celu. Jeśli możesz uruchomić instancję zewnętrznej biblioteki w zone.run() już, możesz nie potrzebować zone.run() później.

 5
Author: Günter Zöchbauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-15 10:31:57

Moim rozwiązaniem jest

   <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 zmienia się w tym samym czasie co rawLapsData, co daje map kolejną szansę wykrycia zmiany za pomocą prostszego obiektu prymitywnego typu. Nie jest elegancki, ale działa.

 3
Author: Simon H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-14 20:49:48

Użyj ChangeDetectorRef.detectChanges(), aby powiedzieć Angular, aby uruchomił wykrywanie zmian podczas edycji zagnieżdżonego obiektu (który pomija przy sprawdzaniu brudu).

 3
Author: Tim Phillips,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-08 20:34:12

Oto hack, który właśnie wyciągnął mnie z kłopotów z tym.

Więc podobny scenariusz do OP-mam zagnieżdżony Składnik Angular, który potrzebuje danych przekazywanych do niego, ale wejście wskazuje na tablicę i jak wspomniano powyżej, Angular nie widzi zmiany, ponieważ nie sprawdza zawartości tablicy.

Aby to naprawić konwertuję tablicę na łańcuch dla Angular, aby wykryć zmianę, a następnie w zagnieżdżonym komponencie dzielę (',') łańcuch z powrotem na tablicę i jego happy znowu dni.

 2
Author: Graham Phillips,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-02 22:40:06

W przypadku tablic można to zrobić tak:

W pliku .ts (komponent nadrzędny) gdzie aktualizujesz swój rawLapsData Zrób to tak:

rawLapsData = somevalue; // change detection will not happen

Rozwiązanie:

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

I ngOnChanges będą wywoływane w komponencie potomnym

 1
Author: Danish Dullu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-20 12:48:28

Wykrywanie zmian nie jest uruchamiane po zmianie Właściwości obiektu (w tym obiektu zagnieżdżonego). Jednym z rozwiązań byłoby ponowne przypisanie nowego odniesienia do obiektu za pomocą funkcji' lodash ' clone ().

import * as _ from 'lodash';

this.foo = _.clone(this.foo);
 0
Author: Anton Nikprelaj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-11 06:21:28

Natknąłem się na tę samą potrzebę. Dużo na ten temat czytałem, więc oto mój Miedziak na ten temat.

Jeśli chcesz, aby Twoja zmiana była wykrywana podczas push, to będziesz ją miał, gdy zmienisz wartość obiektu wewnątrz, prawda ? I Ty też byś to miał, gdybyś w jakiś sposób usunął obiekty.

Jak już wspomniano, użycie changeDetectionStrategy.onPush

Powiedz, że masz ten komponent, który stworzyłeś, z changeDetectionStrategy.onPush: {]}

<component [collection]="myCollection"></component>

Wtedy naciskasz element i uruchamiasz wykrywanie zmian:

myCollection.push(anItem);
refresh();

Lub usuniesz element i uruchomisz wykrywanie zmian:

myCollection.splice(0,1);
refresh();

Lub zmienisz wartość attrbibute dla elementu i uruchomisz wykrywanie zmiany:

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

Zawartość odświeżania:

refresh() : void {
    this.myCollection = this.myCollection.slice();
}

Metoda slice zwraca dokładnie tę samą tablicę, a znak [ = ] wprowadza do niej nowe odniesienie, uruchamiając wykrywanie zmian za każdym razem, gdy jest potrzebna. Łatwe i czytelne:)

Pozdrawiam,

 0
Author: Deunz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-04 12:49:30