To, co próbuję wdrożyć, to w zasadzie moduł obsługi „po powtórzeniu renderowania zakończonego”. Jestem w stanie wykryć, kiedy to się skończy, ale nie mogę wymyślić, jak wywołać z niej funkcję.
Sprawdź skrzypce: http://jsfiddle.net/paulocoelho/BsMqq/3/
JS
var module = angular.module('testApp', [])
.directive('onFinishRender', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
element.ready(function () {
console.log("calling:"+attr.onFinishRender);
// CALL TEST HERE!
});
}
}
}
});
function myC($scope) {
$scope.ta = [1, 2, 3, 4, 5, 6];
function test() {
console.log("test executed");
}
}
HTML
<div ng-app="testApp" ng-controller="myC">
<p ng-repeat="t in ta" on-finish-render="test()">{{t}}</p>
</div>
Odpowiedź : Działające skrzypce od finishmove: http://jsfiddle.net/paulocoelho/BsMqq/4/
element.ready()
fragmentu? Mam na myśli ... czy jest to jakaś wtyczka jQuery, czy masz ją uruchomić, gdy element będzie gotowy?Odpowiedzi:
Zauważ, że nie użyłem,
.ready()
ale raczej zapakowałem w$timeout
.$timeout
upewnia się, że jest wykonywany, gdy elementy powtarzające się ng NAPRAWDĘ$timeout
zakończą renderowanie (ponieważ wykona się pod koniec bieżącego cyklu podsumowania - i zadzwoni również$apply
wewnętrznie, w przeciwieństwie dosetTimeout
). Więc pong-repeat
zakończeniu używamy$emit
do wysyłania zdarzenia do zewnętrznych zakresów (zakresów rodzeństwa i rodzica).A potem w kontrolerze możesz to złapać za pomocą
$on
:Z HTML, który wygląda mniej więcej tak:
źródło
$timeout
(co w zasadziesetTimeout
+ Angulara$scope.$apply()
) zsetInterval
.$timeout
wykona się tylko raz dla danegoif
warunku i będzie to na początku następnego$digest
cyklu. Aby uzyskać więcej informacji na temat limitów czasu JavaScript, patrz: ejohn.org/blog/how-javascript-timers-worksetTimeout()
z opóźnieniem 0, to kolejne pytanie, ale muszę powiedzieć, że nigdy nie spotkałem się z rzeczywistą specyfikacją kolejki zdarzeń przeglądarki, tylko co sugeruje jednowątkowość i istnieniesetTimeout()
.on-finish-render="ngRepeatFinished"
? Kiedy przydzielamon-finish-render="helloworld"
, działa tak samo.Użyj $ evalAsync, jeśli chcesz, aby Twoje wywołanie zwrotne (tj. Test ()) było wykonywane po zbudowaniu DOM, ale przed renderowaniem przeglądarki. Zapobiegnie to migotaniu - ref .
Fiddle .
Jeśli naprawdę chcesz oddzwonić po renderowaniu, użyj $ timeout:
Wolę $ eval zamiast zdarzenia. W przypadku zdarzenia musimy znać nazwę zdarzenia i dodać kod do naszego kontrolera dla tego zdarzenia. Dzięki $ eval istnieje mniej sprzężenia między sterownikiem a dyrektywą.
źródło
$last
? Nie można tego znaleźć w dokumentacji. Czy został usunięty?$last
jest zdefiniowany w zakresie, jeśli elementng-repeat
jest aktywny w elemencie.$timeout
.Odpowiedzi, które zostały podane do tej pory, zadziałają tylko przy pierwszym
ng-repeat
renderowaniu , ale jeśli masz dynamikęng-repeat
, co oznacza, że będziesz dodawać / usuwać / filtrować elementy i za każdym razem musisz otrzymywać powiadomieniang-repeat
zostanie renderowany, te rozwiązania nie będą działać.Tak więc, jeśli chcesz być powiadamiany za każdym razem, że
ng-repeat
wystąpią ponownie wydanego właśnie po raz pierwszy i nie znalazłem sposób na to, że to dość „hacky”, ale to będzie działać dobrze, jeśli wiesz, kim jesteś robić. Użyj tego$filter
w swoim,ng-repeat
zanim użyjesz innego$filter
:Będzie
$emit
to wydarzenie wywoływane zangRepeatFinished
każdym razem, gdyng-repeat
zostanie zrenderowany.Jak tego użyć:
ngRepeatFinish
Potrzebuje filtra należy stosować bezpośrednio naArray
lubObject
zdefiniowanych w$scope
, można zastosować inne filtry po.Jak NIE używać:
Nie stosuj najpierw innych filtrów, a następnie zastosuj
ngRepeatFinish
filtr.Kiedy powinienem tego użyć?
Jeśli chcesz zastosować pewne style css do DOM po zakończeniu renderowania listy, ponieważ musisz wziąć pod uwagę nowe wymiary elementów DOM, które zostały ponownie renderowane przez
ng-repeat
. (BTW: tego rodzaju operacje powinny być wykonywane w ramach dyrektywy)Czego NIE ZROBIĆ w funkcji obsługującej
ngRepeatFinished
zdarzenie:Nie wykonuj
$scope.$apply
w tej funkcji, bo umieścisz Angulara w niekończącej się pętli, której Angular nie będzie w stanie wykryć.Nie należy go używać do wprowadzania zmian we
$scope
właściwościach, ponieważ zmiany te nie zostaną odzwierciedlone w widoku do następnej$digest
pętli, a ponieważ nie można ich wykonać,$scope.$apply
nie będą one przydatne.„Ale filtrów nie należy tak używać !!”
Nie, nie są, to jest hack, jeśli ci się nie podoba, nie używaj go. Jeśli znasz lepszy sposób na osiągnięcie tego samego, daj mi znać.
Zreasumowanie
źródło
$last
element również się zmienia, prostangRepeatFinish
dyrektywa poprzez sprawdzenie$last
teraz będzie działać. Jak tylko zadzwoni ngRepeatFinish, usuwam atrapę. (Ukryłem go również za pomocą CSS, więc nie pojawia się na krótko)Mark Rajcok
działa idealnie przy każdym ponownym renderowaniu powtórzenia ng (np. Z powodu zmiany modelu). Więc to hacky rozwiązanie nie jest wymagane.Jeśli chcesz wywołać różne funkcje dla różnych powtórzeń ng na tym samym kontrolerze, możesz spróbować czegoś takiego:
Dyrektywa:
W swoim kontrolerze łap wydarzenia za pomocą $ on:
W szablonie z wieloma powtórzeniami ng
źródło
Inne rozwiązania będą działały poprawnie przy początkowym ładowaniu strony, ale wywołanie $ timeout z kontrolera jest jedynym sposobem, aby upewnić się, że twoja funkcja jest wywoływana, gdy model się zmienia. Oto działające skrzypce, które wykorzystują limit czasu $. Na przykład będzie to:
ngRepeat oceni dyrektywę tylko wtedy, gdy zawartość wiersza jest nowa, więc jeśli usuniesz elementy z listy, onFinishRender nie uruchomi się. Na przykład spróbuj wprowadzić wartości filtrów w tych skrzypcach emitowanych .
źródło
ta
w$scope.$watch("ta",...
środkuJeśli nie masz nic przeciwko używaniu rekwizytów za dwa dolary i piszesz dyrektywę, której jedyną treścią jest powtórzenie, istnieje całkiem proste rozwiązanie (zakładając, że zależy ci tylko na początkowym renderowaniu). W funkcji łącza:
Jest to przydatne w przypadkach, gdy masz dyrektywę, która musi wykonywać manipulacje DOM na podstawie szerokości lub wysokości elementów renderowanej listy (co moim zdaniem jest najbardziej prawdopodobnym powodem, dla którego można by zadać to pytanie), ale nie jest tak ogólne jak inne zaproponowane rozwiązania.
źródło
Rozwiązaniem tego problemu może być filtrowane ngRepeat zdarzenia mutacji , ale są one przestarzałe (bez natychmiastowej zamiany).
Potem pomyślałem o innym łatwym:
To łączy się z metodami Node.prototype elementu nadrzędnego z limitem czasu jednego kliknięcia $, aby obserwować kolejne modyfikacje.
Działa głównie poprawnie, ale dostałem kilka przypadków, w których altDone byłby wywoływany dwukrotnie.
Znowu ... dodaj tę dyrektywę do elementu nadrzędnego ngRepeat.
źródło
appendChild
(ponieważ użyłem tylkoinsertBefore
iremoveChild
) w powyższym kodzie.Bardzo łatwo, tak to zrobiłem.
źródło
Proszę spojrzeć na skrzypce, http://jsfiddle.net/yNXS2/ . Ponieważ tworzona dyrektywa nie stworzyła nowego zakresu, kontynuowałem.
$scope.test = function(){...
sprawiło, że tak się stało.źródło
Jestem bardzo zaskoczony, że nie znalazłem najprostszego rozwiązania wśród odpowiedzi na to pytanie. To, co chcesz zrobić, to dodać
ngInit
dyrektywę do powtarzanego elementu (elementu zngRepeat
dyrektywą) sprawdzając$last
(specjalną zmienną ustawioną w zakresie,ngRepeat
która wskazuje, że powtarzany element jest ostatnim na liście). Jeśli$last
to prawda, renderujemy ostatni element i możemy wywołać żądaną funkcję.Pełny kod znaczników HTML to:
Nie potrzebujesz dodatkowego kodu JS w swojej aplikacji oprócz funkcji zasięgu, którą chcesz wywołać (w tym przypadku
test
), ponieważngInit
jest ona dostarczana przez Angular.js. Po prostu upewnij się, że masztest
funkcję w zakresie, aby można było uzyskać do niej dostęp z szablonu:źródło