Wyjaśnij mi, dlaczego wciąż pojawia się ten błąd: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Oczywiście dostaję to tylko w trybie deweloperskim, nie dzieje się to w mojej wersji produkcyjnej, ale jest to bardzo denerwujące i po prostu nie rozumiem korzyści płynących z błędu w moim środowisku programistycznym, który nie pojawia się w prod - -prawdopodobnie z powodu mojego braku zrozumienia.
Zwykle poprawka jest dość łatwa, po prostu zawijam błąd powodujący kod w setTimeout w następujący sposób:
setTimeout(()=> {
this.isLoading = true;
}, 0);
Lub wymuś wykrywanie zmian za pomocą takiego konstruktora constructor(private cd: ChangeDetectorRef) {}
:
this.isLoading = true;
this.cd.detectChanges();
Ale dlaczego ciągle mam ten błąd? Chcę to zrozumieć, aby w przyszłości uniknąć tych niepotrzebnych poprawek.
źródło
Odpowiedzi:
Miałem podobny problem. Patrząc na dokumentacji haczykami cyklu życia , zmieniłem
ngAfterViewInit
sięngAfterContentInit
i to zadziałało.źródło
setTimeout
pokazanym wyżej sposobem załatwiło sprawę. Dzięki!ngAfterContentChecked
działa tutaj, podczas gdyngAfterContentInit
wciąż zgłasza błąd.Ten błąd wskazuje na prawdziwy problem w Twojej aplikacji, dlatego sensowne jest zgłoszenie wyjątku.
W
devMode
zmianie wykrywanie dodaje dodatkową turę po każdym biegu regularnego wykrywania zmian, by sprawdzić, czy model się nie zmieniło.Jeśli model zmienił się między zwykłym a dodatkowym zakrętem wykrywania zmian, oznacza to, że albo
które są złe, ponieważ nie jest jasne, jak postępować, ponieważ model może nigdy się nie ustabilizować.
Jeśli wykrywanie kątowe zmienia się, dopóki model się nie ustabilizuje, może działać wiecznie. Jeśli Angular nie uruchomi wykrywania zmian, widok może nie odzwierciedlać bieżącego stanu modelu.
Zobacz także Jaka jest różnica między trybem produkcyjnym a programistycznym w Angular2?
źródło
ngOnInit
lubngOnChanges
modyfikacja modelu (niektóre wywołania cyklu życia pozwalają modyfikować model, inni tego nie robią, sam nie pamiętam, które dokładnie robią, czy nie). Nie łącz się z metodami lub funkcjami w widoku, zamiast tego łącz się z polami i aktualizuj pola w procedurach obsługi zdarzeń. Jeśli musisz powiązać metody, upewnij się, że zawsze zwracają tę samą instancję wartości, o ile w rzeczywistości nie nastąpiła zmiana. Wykrywanie zmian wywoła te metody bardzo często.changeRef.detectChanges()
jest rozwiązaniem / tłumi błąd, jest tego dowodem. To jest jak zmiana stanu wewnątrz$scope.$watch()
Angulara 1.cdRef.detectChanges()
jest konieczny tylko w niektórych dziwnych przypadkach i powinieneś uważnie patrzeć, kiedy potrzebujesz, aby dobrze zrozumieć, dlaczego.Dużo zrozumienia przyszło, gdy zrozumiałem Haki Angular Lifecycle i ich związek z wykrywaniem zmian.
Próbowałem przekonać Angulara do zaktualizowania flagi globalnej powiązanej
*ngIf
z elementem i próbowałem zmienić tę flagę wewnątrzngOnInit()
haka cyklu życia innego komponentu.Zgodnie z dokumentacją ta metoda jest wywoływana po wykryciu zmian przez Angulara:
Tak więc aktualizacja flagi wewnątrz
ngOnChanges()
nie zainicjuje wykrywania zmian. Następnie, gdy wykrycie zmiany zostanie ponownie naturalnie uruchomione, wartość flagi zmieniła się i błąd zostanie zgłoszony.W moim przypadku zmieniłem to:
Do tego:
i naprawił problem :)
źródło
router.navigate
) po załadowaniu do fragmentu, jeśli jest obecny w adresie URL. Ten kod został początkowo umieszczony w miejscu, wAfterViewInit
którym otrzymywałem błąd, a potem przeniosłem się, jak mówisz do konstruktora, ale nie respektował fragmentu. Przeprowadzka dongOnInit
rozwiązania :) dzięki!setInterval()
działa również wtedy, gdy musi zostać uruchomiony po innym kodzie zdarzenia dożywotniego.Aktualizacja
Zdecydowanie polecam zacząć od samooceny PO w pierwszej kolejności: właściwie zastanów się, co można zrobić w
constructor
porównaniu z tym, co należy zrobićngOnChanges()
.Oryginalny
To jest raczej notatka poboczna niż odpowiedź, ale może komuś pomóc. Natknąłem się na ten problem, próbując uzależnić obecność przycisku od stanu formularza:
O ile mi wiadomo, ta składnia prowadzi do dodawania i usuwania przycisku z DOM na podstawie warunku. Co z kolei prowadzi do
ExpressionChangedAfterItHasBeenCheckedError
.Poprawką w moim przypadku (chociaż nie twierdzę, że rozumiem pełne implikacje różnicy) było użycie
display: none
zamiast tego:źródło
[hidden]
zamiast bardzo pełnej[style.display]
części. :)[hidden]
nie zawsze będzie miało takie samo zachowanie jakdisplay: none
Były ciekawe odpowiedzi, ale nie znalazłem takiej, która pasowałaby do moich potrzeb, najbliższa z @ chittrang-mishra, która odnosi się tylko do jednej konkretnej funkcji, a nie kilku przełączników jak w mojej aplikacji.
Nie chciałem
[hidden]
skorzystać z*ngIf
tego, że nie jestem nawet częścią DOM, więc znalazłem następujące rozwiązanie, które może nie być najlepsze dla wszystkich, ponieważ pomija błąd zamiast go poprawiać, ale w moim przypadku, w którym znam końcowy wynik jest poprawny, wydaje się być odpowiedni dla mojej aplikacji.To, co zrobiłem, to wdrożenie
AfterViewChecked
, dodanie,constructor(private changeDetector : ChangeDetectorRef ) {}
a następnieMam nadzieję, że to pomaga innym, podobnie jak wielu innym.
źródło
Wykrywanie zmian przebiegów kątowych, a gdy okaże się, że niektóre wartości, które zostały przekazane do komponentu potomnego, zostały zmienione, kątowy zgłasza błąd:
ExpressionChangedAfterItHasBeenCheckedError
kliknij po więcejAby to naprawić, możemy użyć
AfterContentChecked
haka cyklu życia iźródło
W moim przypadku miałem ten problem w pliku specyfikacji podczas uruchamiania testów.
Musiałem się zmienić
ngIf
na[hidden]
do
źródło
[hidden]
: talkingdotnet.com/dont-use-hidden-attribute-angularjs-2*ngIf
zmienia DOM, dodając i usuwając element ze strony, a jednocześnie[hidden]
zmienia widoczność elementu, nie usuwając go z DOM.Wykonaj poniższe kroki:
1. Użyj „ChangeDetectorRef”, importując go z @ angular / core w następujący sposób:
2. Zaimplementuj go w konstruktorze () w następujący sposób:
3. Dodaj następującą metodę do funkcji, którą wywołujesz w przypadku zdarzenia, takiego jak kliknięcie przycisku. Wygląda to tak:
źródło
Ja pomocą NG2-carouselamos (kąt 8 i bootstrap 4)
Poniżej naprawiłem mój problem:
Co ja zrobiłem:
źródło
Napotkałem ten sam problem, ponieważ wartość zmieniała się w jednej z tablic mojego komponentu. Ale zamiast wykrywać zmiany związane ze zmianą wartości, zmieniłem strategię wykrywania zmian składników na
onPush
(która wykryje zmiany związane ze zmianą obiektu, a nie ze zmianą wartości).źródło
Odnosząc się do artykułu https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
Tak więc mechanika wykrywania zmian działa w taki sposób, że zarówno wykrywanie zmian, jak i podsumowania weryfikacji są wykonywane synchronicznie. Oznacza to, że jeśli zaktualizujemy właściwości asynchronicznie, wartości nie zostaną zaktualizowane podczas działania pętli weryfikacyjnej i nie wystąpi
ExpressionChanged...
błąd. Przyczyną tego błędu jest to, że podczas procesu weryfikacji Angular widzi inne wartości niż to, co zarejestrował podczas fazy wykrywania zmiany. Aby tego uniknąć ...1) Użyj changeDetectorRef
2) użyj setTimeOut. Spowoduje to wykonanie kodu na innej maszynie wirtualnej jako makropolecenie. Angular nie zobaczy tych zmian podczas procesu weryfikacji i nie pojawi się ten błąd.
3) Jeśli naprawdę chcesz wykonać swój kod na tej samej maszynie wirtualnej, użyj jak
To stworzy mikro-zadanie. Kolejka mikroprocesorów jest przetwarzana po zakończeniu wykonywania bieżącego kodu synchronicznego, dlatego aktualizacja właściwości nastąpi po kroku weryfikacji.
źródło
@HostBinding
może być mylącym źródłem tego błędu.Załóżmy na przykład, że masz następujące powiązanie hosta w komponencie
Dla uproszczenia załóżmy, że ta właściwość jest aktualizowana za pomocą następującej właściwości wejściowej:
W komponencie nadrzędnym programujesz go programowo
ngAfterViewInit
Oto co się dzieje:
carousel
(przez ViewChild)carousel
dongAfterViewInit()
(będzie zerowy)style_groupBG = 'red'
background: red
składnik Host ImageCarouselcarousel.style.background
i nie jest wystarczająco sprytny, aby wiedzieć, że nie jest to problem, więc zgłasza wyjątek.Jednym z rozwiązań jest wprowadzenie kolejnej owiniętej okładki ImageCarousel i ustawienie na niej koloru tła, ale wtedy nie uzyskasz niektórych korzyści z używania
HostBinding
(takich jak umożliwienie rodzicowi kontrolowania pełnych granic obiektu).Lepszym rozwiązaniem w komponencie nadrzędnym jest dodanie funkcji wykrywaniaChanges () po ustawieniu konfiguracji.
Może to wyglądać dość oczywiste w ten sposób i bardzo podobne do innych odpowiedzi, ale istnieje subtelna różnica.
Rozważ przypadek, gdy nie dodasz go
@HostBinding
później podczas programowania. Nagle pojawia się ten błąd i wydaje się, że nie ma to żadnego sensu.źródło
Oto moje przemyślenia na temat tego, co się dzieje. Nie przeczytałem dokumentacji, ale jestem pewien, że jest to część powodu, dla którego wyświetlany jest błąd.
Gdy używasz * ngIf, fizycznie zmienia DOM, dodając lub usuwając element za każdym razem, gdy zmienia się warunek. Jeśli więc warunek zmieni się, zanim zostanie wyświetlony w widoku (co jest wysoce możliwe w świecie Angulara), błąd zostanie zgłoszony. Patrz wyjaśnienia tutaj między trybami rozwoju i produkcji.
Podczas używania
[hidden]
nie zmienia fizycznie,DOM
ale jedynie ukrywaelement
widok, najprawdopodobniej używającCSS
z tyłu. Element jest nadal obecny w DOM, ale niewidoczny w zależności od wartości warunku. Dlatego błąd nie wystąpi podczas używania[hidden]
.źródło
isProcessing()
robi się coś ame, należy użyć!isProcessing()
dla[hidden]
hidden
„nie używa CSS z tyłu”, to zwykła właściwość HTML. developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/…W przypadku mojego problemu czytałem github - „ExpressionChangedAfterItHasBeenCheckedError podczas zmiany wartości składnika„ nie modelowego ”w afterViewInit” i postanowiłem dodać model ngModel
Naprawił mój problem, mam nadzieję, że komuś pomoże.
źródło
ngModel
. Czy możesz wyjaśnić, dlaczego to powinno być pomocne?Wskazówki dotyczące debugowania
Ten błąd może być dość mylący i łatwo jest błędnie założyć, kiedy dokładnie się pojawia. Przydaje mi się dodawanie wielu takich instrukcji debugowania do odpowiednich komponentów w odpowiednich miejscach. Pomaga to zrozumieć przepływ.
W rodzicu umieść takie instrukcje (dokładny ciąg „EXPRESSIONCHANGED” jest ważny), ale poza tym są to tylko przykłady:
W wywołaniach potomnych / usługach / odliczaniu czasu:
Jeśli uruchomisz
detectChanges
ręcznie, dodaj również rejestrowanie:Następnie w debuggerze Chrome po prostu filtruj według „ZMIANY WYRAŻANIA”. To pokaże dokładnie przepływ i kolejność wszystkiego, co zostanie ustawione, a także dokładnie w którym momencie Angular zgłasza błąd.
Możesz także kliknąć szare linki, aby wstawić punkty przerwania.
Kolejną rzeczą, na którą należy zwrócić uwagę, jeśli masz podobnie nazwane właściwości w swojej aplikacji (np.
style.background
), Upewnij się, że debugujesz tę, o której myślisz - ustawiając ją na niejasną wartość koloru.źródło
W moim przypadku miałem właściwość asynchroniczną
LoadingService
z obiektem BehaviouralSubjectisLoading
Korzystanie z [ukrytego] modelu działa, ale * ngIf zawiedzie
źródło
Rozwiązanie, które działało dla mnie przy użyciu rxjs
źródło
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
że zmiany wartości są wyzwalane, gdy zmienia się DOMdelay
sprawiają, że błąd zniknie. Działa podobnie dosetTimeout
.Miałem tego rodzaju błąd w Ionic3 (który używa Angulara 4 jako części stosu technologii).
Dla mnie robił to:
<ion-icon [name]="getFavIconName()"></ion-icon>
Próbowałem więc warunkowo zmienić typ ikony jonowej z a
pin
na aremove-circle
, za trybie ekran działał dalej.Chyba będę musiał dodać
*ngIf
zamiast tego.źródło
Mój problem pojawił się, gdy dodałem,
*ngIf
ale to nie była przyczyna. Błąd został spowodowany przez zmianę modelu w{{}}
znacznikach, a następnie próbę wyświetlenia zmienionego modelu w*ngIf
instrukcji później. Oto przykład:Aby rozwiązać problem, zmieniłem miejsce, w którym dzwoniłem
changeMyModelValue()
do miejsca, które ma większy sens.W mojej sytuacji chciałem
changeMyModelValue()
zadzwonić, ilekroć element podrzędny zmienił dane. Wymagało to utworzenia i emisji zdarzenia w komponencie potomnym, aby rodzic mógł go obsłużyć (dzwoniącchangeMyModelValue()
. Patrz https://angular.io/guide/component-interaction#parent-listens-for-child-eventźródło
Mam nadzieję, że to pomoże komuś, kto tu przyjdzie: Wykonujemy zgłoszenia serwisowe w
ngOnInit
następujący sposób i używamy zmiennejdisplayMain
do kontrolowania montażu elementów w DOM.component.ts
i component.html
źródło
Wystąpił ten błąd, ponieważ korzystałem ze zmiennej w pliku component.html, która nie została zadeklarowana w pliku component.ts. Po usunięciu części w HTML błąd zniknął.
źródło
Wystąpił ten błąd, ponieważ wysyłałem akcje redux w trybie modalnym, a modal nie został w tym czasie otwarty. Wysyłałem akcje w chwili, gdy składnik modalny otrzymuje dane wejściowe. Dlatego umieściłem tam setTimeout, aby upewnić się, że modal jest otwarty, a następnie akcje są wysyłane.
źródło
Dla każdego, kto ma z tym problem. Oto sposób prawidłowego debugowania tego błędu: https://blog.angular-university.io/angular-debugging/
W moim przypadku rzeczywiście pozbyłem się tego błędu, używając tego [ukrytego] hacka zamiast * ngIf ...
Ale link I, pod warunkiem pozwoliło mi znaleźć winnego * ngIf :)
Cieszyć się.
źródło
hidden
zamiastngIf
hakowania nie rozwiązuje w ogóle sedna problemu. Po prostu maskujesz problem.Rozwiązanie ... usługi i rxjs ... emitery zdarzeń i wiązanie właściwości używają rxjs .. lepiej jest wdrożyć to samo, więcej kontroli, łatwiej debugować. Pamiętaj, że emitery zdarzeń używają rxjs. Po prostu stwórz usługę i obserwuj, aby każdy komponent zasubskrybował obserwatora i albo przekaż nową wartość, albo wartość cosume w razie potrzeby
źródło