Mój komponent ma style zależne od bieżącej daty i godziny. W moim komponencie mam następującą funkcję.
private fontColor( dto : Dto ) : string {
// date d'exécution du dto
let dtoDate : Date = new Date( dto.LastExecution );
(...)
let color = "hsl( " + hue + ", 80%, " + (maxLigness - lightnessAmp) + "%)";
return color;
}
lightnessAmp
jest obliczany na podstawie bieżącej daty i godziny. Kolor zmienia się, jeśli dtoDate
wystąpił w ciągu ostatnich 24 godzin.
Dokładny błąd jest następujący:
Wyrażenie zmieniło się po sprawdzeniu. Poprzednia wartość: „hsl (123, 80%, 49%)”. Aktualna wartość: „hsl (123, 80%, 48%)”
Wiem, że wyjątek pojawia się w trybie programistycznym tylko w momencie sprawdzania wartości. Jeśli zaznaczona wartość różni się od zaktualizowanej wartości, zostanie zgłoszony wyjątek.
Próbowałem więc zaktualizować bieżącą datę i godzinę w każdym cyklu życia w następujący sposób, aby zapobiec wyjątkowi:
ngAfterViewChecked()
{
console.log( "! changement de la date du composant !" );
this.dateNow = new Date();
}
... ale bez powodzenia.
angular
typescript
time
styles
components
Anthony Brenelière
źródło
źródło
Odpowiedzi:
Uruchom wykrywanie zmian jawnie po zmianie:
źródło
ngOnInit()
zwykle jest to pierwsze miejsce. Jeśli kod zależy od renderowanego modelu DOM,ngAfterViewInit()
czyngAfterContentInit()
są to następne opcje.ngOnChanges()
jest dobrym rozwiązaniem, jeśli kod powinien być wykonywany za każdym razem, gdy dane wejściowe zostały zmienione.ngDoCheck()
służy do niestandardowego wykrywania zmian. Właściwie nie wiem, do czegongAfterViewChecked()
najlepiej się nadaje. Myślę, że nazywa się to tuż przed lub pongAfterViewInit()
.clientWidth
itp.TL; DR
Chociaż jest to obejście, czasami naprawdę trudno jest rozwiązać ten problem w lepszy sposób, więc nie obwiniaj siebie, jeśli używasz tego podejścia. W porządku.
Przykłady : początkowy problem [ link ], rozwiązany za pomocą
setTimeout()
[ link ]Jak ominąć
Ogólnie ten błąd występuje zwykle po dodaniu czegoś (nawet w komponentach nadrzędnych / podrzędnych)
ngAfterViewInit
. Więc pierwsze pytanie brzmi: czy mogę żyć bezngAfterViewInit
? Być może gdzieś przenosisz kod (ngAfterViewChecked
może to być alternatywa).Przykład : [ link ]
Również
Również rzeczy asynchroniczne,
ngAfterViewInit
które mają wpływ na DOM, mogą to powodować. Można to również rozwiązać za pomocąsetTimeout
lub dodającdelay(0)
operator w potoku:Przykład : [ link ]
Miłe czytanie
Dobry artykuł o tym, jak to debugować i dlaczego tak się dzieje: link
źródło
Jak wspomniał @leocaseiro w sprawie github .
źródło
Jej masz dwa rozwiązania!
1. Zmodyfikuj ChangeDetectionStrategy na OnPush
W przypadku tego rozwiązania w zasadzie podasz kąt:
Szybka naprawa:
Zmodyfikuj swój komponent, aby używał
ChangeDetectionStrategy.OnPush
Dzięki temu rzeczy już nie działają. To dlatego, że od teraz będziesz musiał
detectChanges()
ręcznie wywoływać Angular .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:
Następujące operacje są sprawdzane w cyklu podsumowania:
I tak błąd jest generowany, gdy porównywane wartości są różne. , bloger Max Koretskyi stwierdził:
I na koniec kilka przykładów z rzeczywistego świata, które zwykle powodują ten błąd:
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:
Ten sam blog mówi również:
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ć ):
@Input
i@Output
staraj się unikać wyzwalania zmian w cyklu życia, chyba że składnik zostanie całkowicie zainicjowany.this.cdr.detectChanges();
które mogą powodować więcej błędów, szczególnie gdy masz do czynienia z dużą ilością danych dynamicznychthis.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
)Również
Jeśli chcesz w pełni zrozumieć Angular Life Hook, polecam przeczytanie oficjalnej dokumentacji tutaj:
źródło
ChangeDetectionStrategy
abyOnPush
naprawić go dla mnie. Mam prosty element, który ma[disabled]="isLastPage()"
. Ta metoda odczytujeMatPaginator
-ViewChild i zwracathis.paginator !== undefined ? this.paginator.pageIndex === this.paginator.getNumberOfPages() - 1 : true;
. Paginator nie jest dostępny od razu, ale po powiązaniu z użyciem@ViewChild
. ZmianaChangeDetectionStrategy
usuniętego błędu - a funkcjonalność nadal istnieje jak poprzednio. Nie jestem pewien, jakie mam teraz wady, ale dziękuję!OnPush
jest to, że będziesz musiał używaćthis.cdr.detectChanges()
za każdym razem, gdy chcesz, aby twój komponent był odświeżany. Myślę, że już goW naszym przypadku NAPRAWIONO przez dodanie changeDetection do komponentu i wywołanie funkcji detekcji zmian () w ngAfterContentChecked, kod w następujący sposób
źródło
Myślę, że najlepszym i najczystszym rozwiązaniem, jakie możesz sobie wyobrazić, jest:
Testowane z Angular 5.2.9
źródło
Mała praca, z której korzystałem wiele razy
Głównie zastępuję "null" jakąś zmienną, której używam w kontrolerze.
źródło
Chociaż jest już wiele odpowiedzi i link do bardzo dobrego artykułu o wykrywaniu zmian, chciałem tutaj dać swoje dwa centy. Myślę, że to sprawdzenie ma swój powód, więc pomyślałem o architekturze mojej aplikacji i zdałem sobie sprawę, że zmiany w widoku można rozwiązać, używając
BehaviourSubject
i prawidłowego haka cyklu życia. Oto, co zrobiłem dla rozwiązania.Ostatecznie otrzymałem podstawową klasę JavaScript i muszę zainicjować własny nagłówek kalendarza dla komponentu. Wymaga
ViewChild
to renderowania przed renderowaniem mojego rodzica, co nie jest sposobem, w jaki działa Angular. Dlatego zawarłem wartość, której potrzebuję dla mojego szablonu wBehaviourSubject<View>(null)
:Następnie, gdy mam pewność, że widok jest zaznaczony, aktualizuję ten temat o wartość z
@ViewChild
:Następnie w moim szablonie po prostu używam
async
rury. Bez hakowania z wykrywaniem zmian, bez błędów, działa płynnie.Nie wahaj się zapytać, czy potrzebujesz więcej informacji.
źródło
Użyj domyślnej wartości formularza, aby uniknąć błędu.
Zamiast korzystać z akceptowanej odpowiedzi, jaką jest zastosowanie funkcji detekcji zmian () w ngAfterViewInit () (co również rozwiązało błąd w moim przypadku), zdecydowałem się zamiast tego zapisać wartość domyślną dla dynamicznie wymaganego pola formularza, tak aby przy późniejszej aktualizacji formularza, jego ważność nie jest zmieniana, jeśli użytkownik zdecyduje się zmienić opcję w formularzu, która spowoduje uruchomienie nowych wymaganych pól (i wyłączenie przycisku przesyłania).
Pozwoliło to zaoszczędzić trochę kodu w moim komponencie, aw moim przypadku całkowicie uniknięto błędu.
źródło
this.cdr.detectChanges()
Otrzymałem ten błąd, ponieważ zadeklarowałem zmienną, a później chciałem
zmienić jej wartość za pomocą
ngAfterViewInit
naprawić to, z czego się zmieniłem
do
źródło