Dlaczego jest składnikiem tego prostego wyłożenia
@Component({
selector: 'my-app',
template: `<div>I'm {{message}} </div>`,
})
export class App {
message:string = 'loading :(';
ngAfterViewInit() {
this.updateMessage();
}
updateMessage(){
this.message = 'all done loading :)'
}
}
rzucanie:
WYJĄTEK: Wyrażenie „Jestem {{message}} w aplikacji @ 0: 5” zmieniło się po sprawdzeniu. Poprzednia wartość: „Ładuję :(”. Aktualna wartość: „ładowanie zostało zakończone :)” w [I'm {{message}} w aplikacji @ 0: 5]
kiedy wszystko, co robię, to aktualizowanie prostego wiązania po zainicjowaniu mojego widoku?
typescript
angular
narysował Moore
źródło
źródło
ExpressionChangedAfterItHasBeenCheckedError
błędzie, wyjaśnia szczegółowo to zachowanie.ChangeDetectionStrategy
podczas korzystania zdetectChanges()
stackoverflow.com/questions/39787038/…Odpowiedzi:
Jak stwierdził Drawmoore, właściwym rozwiązaniem w tym przypadku jest ręczne uruchomienie wykrywania zmian dla bieżącego komponentu. Odbywa się to za pomocą
detectChanges()
metodyChangeDetectorRef
obiektu (importowanego zangular2/core
) lub jejmarkForCheck()
metody, która powoduje również aktualizację wszystkich komponentów nadrzędnych. Odpowiedni przykład :Oto również narzędzia Plunkers demonstrujące na wszelki wypadek podejścia ngOnInit , setTimeout i enableProdMode .
źródło
cdr : any
lub coś takiego wmessage
deklaracji. Martwisz się, że coś przeoczyłem?Po pierwsze, zwróć uwagę, że ten wyjątek zostanie zgłoszony tylko wtedy, gdy uruchomisz aplikację w trybie deweloperskim (co ma miejsce domyślnie w wersji beta-0): Jeśli zadzwonisz
enableProdMode()
podczas ładowania aplikacji, nie zostanie ona zgłoszona ( zobacz zaktualizowane plunk ).Po drugie, nie rób tego, ponieważ ten wyjątek jest zgłaszany z ważnego powodu: krótko mówiąc, w trybie deweloperskim po każdej rundzie wykrywania zmian następuje natychmiast druga runda, która weryfikuje, czy żadne powiązania nie uległy zmianie od końca pierwszej, ponieważ oznaczałoby to, że zmiany są powodowane przez samo wykrywanie zmian.
W twojej grze, wiązanie
{{message}}
jest zmieniane przez twoje wezwanie dosetMessage()
, które dzieje się wngAfterViewInit
haku, które występuje jako część początkowej tury wykrywania zmian. To samo w sobie nie jest jednak problematyczne - problem polega na tym, żesetMessage()
zmienia to wiązanie, ale nie wyzwala nowej rundy wykrywania zmian, co oznacza, że ta zmiana nie zostanie wykryta, dopóki jakaś inna runda wykrywania zmian nie zostanie uruchomiona w innym miejscu.Na wynos: Wszystko, co zmienia wiązanie, musi wywołać rundę wykrywania zmian, kiedy to nastąpi .
Zaktualizuj w odpowiedzi na wszystkie prośby o przykład, jak to zrobić : rozwiązanie @ Tycho działa, podobnie jak trzy metody w odpowiedzi @ MarkRajcok wskazał. Ale szczerze mówiąc, wszyscy czują się dla mnie brzydcy i źli, jak hacki, do których przyzwyczailiśmy się w ng1.
Aby mieć pewność, że są sporadyczne sytuacje, w których te hacki są odpowiednie, ale jeśli używasz ich na coś więcej niż tylko bardzo sporadycznie, to znak, że walczysz ramy zamiast całkowicie obejmując jego reaktywny charakter.
IMHO, bardziej idiomatyczny, „Angular2 sposób” podejścia do tego jest coś w stylu: ( plunk )
źródło
detectChanges()
.updateMessage
, a niesetMessage
ngAfterViewChecked()
pracował dla mnie:źródło
Naprawiłem to, dodając ChangeDetectionStrategy z kątowego rdzenia.
źródło
CheckOnce
( dokumentacja )this.cdr.detectChanges();
po tym, co próbujesz załadować. Ponieważ może tak być, ponieważ wykrywanie zmian nie uruchamia sięNie możesz użyć,
ngOnInit
ponieważ właśnie zmieniasz zmienną składowąmessage
?Jeśli chcesz uzyskać dostęp do odwołania do komponentu potomnego
@ViewChild(ChildComponent)
, naprawdę musisz na niego poczekaćngAfterViewInit
.Brudną poprawką jest wywołanie
updateMessage()
kolejnej pętli zdarzeń za pomocą np. SetTimeout.źródło
W tym celu próbowałem powyżej odpowiedzi wiele nie działa w najnowszej wersji Angular (6 lub nowszy)
Korzystam z kontroli materiałów, która wymagała zmian po pierwszym wiązaniu.
Dodając moją odpowiedź, to pomaga niektórym rozwiązać konkretny problem.
źródło
Artykuł Wszystko, co musisz wiedzieć o
ExpressionChangedAfterItHasBeenCheckedError
błędzie, wyjaśnia szczegółowo to zachowanie.Problem z konfiguracją polega na tym, że
ngAfterViewInit
hak cyklu życia jest wykonywany po przetworzeniu aktualizacji DOM przetworzonych zmian wykrywających zmiany. I skutecznie zmieniasz właściwość używaną w szablonie w tym haku, co oznacza, że DOM musi zostać ponownie renderowany:i będzie to wymagało kolejnego cyklu wykrywania zmian, a Angular by design uruchamia tylko jeden cykl podsumowania.
Zasadniczo masz dwie alternatywy, jak to naprawić:
zaktualizuj właściwość asynchronicznie za pomocą
setTimeout
,Promise.then
albo asynchroniczny obserwowalne mowa w szablonieprzed aktualizacją DOM wykonaj aktualizację właściwości w trybie hook - ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked.
źródło
Musisz tylko zaktualizować wiadomość w odpowiednim haku cyklu życia, w tym przypadku jest to
ngAfterContentChecked
zamiastngAfterViewInit
, ponieważ w ngAfterViewInit sprawdzanie wiadomości zmiennej zostało rozpoczęte, ale jeszcze się nie zakończyło.widzieć: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview
więc kod będzie po prostu:
zobacz działające demo na Plunker.
źródło
@ViewChildren()
licznika opartego na długości, który został zapełniony przez Observable. To było jedyne rozwiązanie, które działało dla mnie!ngAfterContentChecked
izChangeDetectorRef
góry działała dla mnie. OnngAfterContentChecked
nazywa -this.cdr.detectChanges();
Ten błąd nadchodzi, ponieważ istniejąca wartość jest aktualizowana natychmiast po zainicjowaniu. Jeśli więc zaktualizujesz nową wartość po wyrenderowaniu istniejącej wartości w DOM, to będzie działać dobrze. Podobnie jak wspomniano w tym artykule Debugowanie kątowe „Wyrażenie zmieniło się po sprawdzeniu”
na przykład możesz użyć
}
lub
Jak widać nie wspomniałem o czasie w metodzie setTimeout. Ponieważ jest to interfejs API dostarczany przez przeglądarkę, a nie interfejs API JavaScript, będzie on działał osobno na stosie przeglądarki i będzie czekał, aż elementy stosu wywołań zostaną zakończone.
W jaki sposób interfejs API przeglądarki wywołuje koncepcję Philipa Robertsa w jednym z filmów na Youtube (Czym jest hack, pętla zdarzeń?).
źródło
Możesz również umieścić swoje wywołanie updateMessage () w ngOnInt () - Metoda, przynajmniej mi to działa
W RC1 nie powoduje to wyjątku
źródło
Możesz również utworzyć licznik czasu za pomocą
Observable.timer
funkcji rxjs , a następnie zaktualizować wiadomość w swojej subskrypcji:źródło
Zgłasza błąd, ponieważ kod jest aktualizowany po wywołaniu funkcji ngAfterViewInit () . Oznacza to, że twoja wartość początkowa uległa zmianie, gdy ma miejsce ngAfterViewInit. Jeśli wywołasz to w ngAfterContentInit (), nie spowoduje to błędu.
źródło
Nie mogłem skomentować posta @Biranchi, ponieważ nie mam wystarczającej reputacji, ale rozwiązało to problem.
Jedną rzecz do zapamiętania! Jeśli dodajesz changeDetection: ChangeDetectionStrategy.OnPush na komponencie nie działał, a jego komponent potomny (głupi komponent) spróbuj również dodać go do elementu nadrzędnego.
To naprawiło błąd, ale zastanawiam się, jakie są tego skutki uboczne.
źródło
Mam podobny błąd podczas pracy z danymi. Dzieje się tak, gdy używasz * ngFor w innym * ngForable danych rzucić ten błąd, ponieważ przerywa cykl zmiany kąta. SO zamiast korzystać z bazy danych wewnątrz bazy danych, użyj jednej zwykłej tabeli lub zamień mf.data na nazwę tablicy. To działa dobrze.
źródło
Myślę, że najprostszym rozwiązaniem byłoby:
(static working: boolean)
w klasie, w której ta funkcja istnieje i za każdym razem, gdy wywołujesz funkcję, po prostu spraw, aby była prawdziwa, zależnie od tego, co chcesz. W ramach funkcji, jeśli wartość pracy jest prawdziwa, po prostu wróć od razu bez robienia czegokolwiek. w przeciwnym razie wykonaj żądane zadanie. Pamiętaj, aby zmienić tę zmienną na false po zakończeniu zadania, tj. Na końcu wiersza kodów lub w metodzie subskrypcji, gdy skończysz przypisywanie wartości!źródło