Jej masz dwa rozwiązania!
1. Zmodyfikuj ChangeDetectionStrategy na OnPush
W przypadku tego rozwiązania w zasadzie podasz kąt:
Przestań sprawdzać zmiany; zrobię to tylko wtedy, gdy wiem, że jest to konieczne
Szybka naprawa:
Zmodyfikuj swój komponent, aby używał ChangeDetectionStrategy.OnPush
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
// ...
}
Dzięki temu rzeczy już nie działają. To dlatego, że od teraz będziesz musiał detectChanges()
ręcznie wywoływać Angular .
this.cdr.detectChanges();
Oto link, który pomógł mi dobrze zrozumieć ChangeDetectionStrategy :
https://alligator.io/angular/change-detection-strategy/
2. Zrozumienie ExpressionChangedAfterItHasBeenCheckedError
Oto mały fragment odpowiedzi tomonari_t na temat przyczyn tego błędu, próbowałem zawrzeć tylko te części, które pomogły mi to zrozumieć.
Pełny artykuł zawiera prawdziwe przykłady kodu dotyczące każdego punktu pokazanego tutaj.
Główną przyczyną jest kątowy cykl życia:
Po każdej operacji Angular pamięta, jakich wartości użył do wykonania operacji. Są one przechowywane we właściwości oldValues widoku komponentu.
Po sprawdzeniu wszystkich składników, Angular rozpoczyna następny cykl podsumowania, ale zamiast wykonywać operacje, porównuje bieżące wartości z wartościami, które pamięta z poprzedniego cyklu podsumowania.
Następujące operacje są sprawdzane w cyklu podsumowania:
sprawdź, czy wartości przekazywane do komponentów podrzędnych są takie same, jak wartości, które byłyby teraz używane do aktualizacji właściwości tych komponentów.
sprawdź, czy wartości używane do aktualizacji elementów DOM są takie same, jak wartości, które byłyby używane do aktualizacji tych elementów, teraz działają tak samo.
sprawdza wszystkie komponenty potomne
I tak błąd jest generowany, gdy porównywane wartości są różne. , bloger Max Koretskyi stwierdził:
Winowajcą jest zawsze składnik potomny lub dyrektywa.
I na koniec kilka przykładów z rzeczywistego świata, które zwykle powodują ten błąd:
- Usługi wspólne
- Synchroniczne nadawanie wydarzeń
- Dynamiczne tworzenie instancji komponentów
Każdy przykład można znaleźć tutaj (plunkr), w moim przypadku problemem była dynamiczna instancja komponentu.
Również z własnego doświadczenia zdecydowanie polecam wszystkim unikanie setTimeout
rozwiązania, w moim przypadku spowodowało to "prawie" nieskończoną pętlę (21 wezwań, których nie jestem skłonny pokazać, jak je sprowokować),
Radziłbym zawsze pamiętać o cyklu życia Angulara, abyś mógł wziąć pod uwagę, jak wpłynie to na nie za każdym razem, gdy modyfikujesz wartość innego komponentu. Z tym błędem Angular mówi ci:
Może robisz to w niewłaściwy sposób, czy na pewno masz rację?
Ten sam blog mówi również:
Często rozwiązaniem jest użycie odpowiedniego zaczepu do wykrywania zmian w celu utworzenia komponentu dynamicznego
Krótkim przewodnikiem dla mnie jest rozważenie co najmniej dwóch następujących rzeczy podczas kodowania ( z czasem postaram się to uzupełnić ):
- Unikaj modyfikowania wartości komponentu nadrzędnego z jego komponentów podrzędnych, zamiast tego: modyfikuj je z jego rodzica.
- Podczas korzystania z dyrektyw
@Input
i @Output
staraj się unikać wyzwalania zmian w cyklu życia, chyba że składnik zostanie całkowicie zainicjowany.
- Unikaj niepotrzebnych wywołań,
this.cdr.detectChanges();
które mogą powodować więcej błędów, szczególnie gdy masz do czynienia z dużą ilością danych dynamicznych
- Gdy użycie
this.cdr.detectChanges();
jest obowiązkowe, upewnij się, że używane zmienne ( @Input, @Output, etc
) są wypełnione / zainicjowane na prawym haku wykrywania ( OnInit, OnChanges, AfterView, etc
)
- Jeśli to możliwe, raczej usuń niż ukryj , jest to związane z punktem 3 i 4.
Również
Jeśli chcesz w pełni zrozumieć Angular Life Hook, polecam przeczytanie oficjalnej dokumentacji tutaj: