Wiele odpowiedzi tutaj zawiera dobre porady, ale może również prowadzić do zamieszania. Samo użycie nie$timeout
jest najlepszym ani właściwym rozwiązaniem. Pamiętaj również, aby przeczytać tę informację, jeśli niepokoi Cię wydajność lub skalowalność.
Rzeczy, które powinieneś wiedzieć
$$phase
jest prywatny dla ram i istnieją ku temu dobre powody.
$timeout(callback)
poczeka, aż zakończy się bieżący cykl podsumowania (jeśli istnieje), a następnie wykona wywołanie zwrotne, a następnie uruchom na końcu pełny $apply
.
$timeout(callback, delay, false)
zrobi to samo (z opcjonalnym opóźnieniem przed wykonaniem wywołania zwrotnego), ale nie uruchomi $apply
(trzeciego argumentu), który zapisuje wydajność, jeśli nie zmodyfikujesz modelu Angular ($ scope).
$scope.$apply(callback)
wywołuje, między innymi $rootScope.$digest
, co oznacza, że ponownie przekopiuje zakres główny aplikacji i wszystkich jej potomków, nawet jeśli jesteś w zakresie izolowanym.
$scope.$digest()
po prostu zsynchronizuje swój model z widokiem, ale nie przetrawi zakresu nadrzędnego, co może zaoszczędzić wiele wydajności podczas pracy nad izolowaną częścią twojego HTML z izolowanym zakresem (głównie z dyrektywy). $ digest nie odbiera oddzwonienia: wykonujesz kod, a następnie skrót.
$scope.$evalAsync(callback)
został wprowadzony z angularjs 1.2 i prawdopodobnie rozwiąże większość twoich problemów. Więcej informacji na ten temat można znaleźć w ostatnim akapicie.
jeśli dostaniesz $digest already in progress error
, oznacza to, że twoja architektura jest zła: albo nie musisz przekierowywać swojego zakresu, albo nie powinieneś być za to odpowiedzialny (patrz poniżej).
Jak zbudować swój kod
Gdy pojawia się ten błąd, próbujesz przetrawić swój zakres, gdy jest już w toku: ponieważ nie znasz stanu swojego zakresu w tym momencie, nie jesteś odpowiedzialny za jego trawienie.
function editModel() {
$scope.someVar = someVal;
/* Do not apply your scope here since we don't know if that
function is called synchronously from Angular or from an
asynchronous code */
}
// Processed by Angular, for instance called by a ng-click directive
$scope.applyModelSynchronously = function() {
// No need to digest
editModel();
}
// Any kind of asynchronous code, for instance a server request
callServer(function() {
/* That code is not watched nor digested by Angular, thus we
can safely $apply it */
$scope.$apply(editModel);
});
A jeśli wiesz, co robisz i pracujesz nad izolowaną małą dyrektywą, będąc częścią dużej aplikacji Angular, możesz zamiast $ zastosować zamiast skrótu zastosować, aby zapisać wyniki.
Aktualizacja od Angularjs 1.2
Nowa, skuteczna metoda została dodana do jakiejkolwiek $ zakresie: $evalAsync
. Zasadniczo wykona on wywołanie zwrotne w ramach bieżącego cyklu podsumowania, jeśli taki wystąpi, w przeciwnym razie nowy cykl podsumowania rozpocznie wykonywanie wywołania zwrotnego.
To nadal nie jest tak dobre, jak $scope.$digest
jeśli naprawdę wiesz, że musisz zsynchronizować tylko izolowaną część kodu HTML (ponieważ nowa $apply
zostanie uruchomiona, jeśli żadna nie jest w toku), ale jest to najlepsze rozwiązanie, gdy wykonujesz funkcję którego nie możesz wiedzieć, czy będzie on wykonywany synchronicznie, czy nie , na przykład po pobraniu zasobu potencjalnie buforowanego: czasami będzie to wymagać asynchronicznego połączenia z serwerem, w przeciwnym razie zasób zostanie lokalnie pobrany synchronicznie.
W tych przypadkach i we wszystkich innych, w których miałeś !$scope.$$phase
, koniecznie użyj$scope.$evalAsync( callback )