Wysyłanie zdarzenia po zakończeniu ładowania AngularJS

114

Zastanawiałem się, jaki jest najlepszy sposób na wykrycie zakończenia ładowania strony / ładowania początkowego, gdy wszystkie dyrektywy zakończyły kompilację / linkowanie.

Jakieś wydarzenie już tam jest? Czy powinienem przeciążać funkcję bootstrap?

Lior
źródło

Odpowiedzi:

204

Tylko przeczucie: dlaczego nie przyjrzeć się, jak to robi dyrektywa ngCloak? Oczywiście dyrektywa ngCloak zarządza wyświetlaniem zawartości po załadowaniu rzeczy. Założę się, że spojrzenie na ngCloak doprowadzi do dokładnej odpowiedzi ...

EDYCJA 1 godzinę później: Ok, cóż, spojrzałem na ngCloak i jest naprawdę krótki. To oczywiście oznacza, że ​​funkcja kompilująca nie zostanie wykonana, dopóki wyrażenia {{template}} nie zostaną ocenione (tj. Załadowany szablon), stąd fajna funkcjonalność dyrektywy ngCloak.

Moim świadomym przypuszczeniem byłoby po prostu utworzenie dyrektywy z taką samą prostotą jak ngCloak, a następnie w funkcji kompilującej robienie wszystkiego, co chcesz. :) Umieść dyrektywę w elemencie głównym swojej aplikacji. Możesz wywołać dyrektywę w stylu myOnload i użyć jej jako atrybutu my-onload. Funkcja kompilacji zostanie wykonana po skompilowaniu szablonu (oszacowaniu wyrażeń i załadowaniu pod-szablonów).

EDYCJA, 23 godziny później: Ok, więc poszukałem informacji i zadałem własne pytanie . Pytanie, które zadałem, było pośrednio związane z tym pytaniem, ale przypadkowo doprowadziło mnie do odpowiedzi, która rozwiązuje to pytanie.

Odpowiedź jest taka, że ​​możesz utworzyć prostą dyrektywę i umieścić swój kod w funkcji link dyrektywy, która (w większości przypadków użycia, wyjaśnione poniżej) będzie działać, gdy element będzie gotowy / załadowany. Na podstawie opisu kolejności wykonywania funkcji kompilacji i łączenia przez Josha ,

jeśli masz ten znacznik:

<div directive1>
  <div directive2>
    <!-- ... -->
  </div>
</div>

Następnie AngularJS utworzy dyrektywy, uruchamiając funkcje dyrektyw w określonej kolejności:

directive1: compile
  directive2: compile
directive1: controller
directive1: pre-link
  directive2: controller
  directive2: pre-link
  directive2: post-link
directive1: post-link

Domyślnie prosta funkcja "link" jest post-linkem, więc funkcja linkująca z zewnętrznej dyrektywy1 nie będzie działać, dopóki nie zostanie uruchomiona funkcja linkująca wewnętrznej dyrektywy2. Dlatego mówimy, że manipulowanie DOMem jest bezpieczne tylko w post-link. Zatem w stosunku do pierwotnego pytania nie powinno być problemu z dostępem do wewnętrznego kodu HTML dyrektywy potomnej z funkcji link dyrektywy zewnętrznej, chociaż dynamicznie wstawiana zawartość musi zostać skompilowana, jak wspomniano powyżej.

Z tego możemy wywnioskować, że możemy po prostu stworzyć dyrektywę, aby wykonać nasz kod, gdy wszystko jest gotowe / skompilowane / połączone / załadowane:

    app.directive('ngElementReady', [function() {
        return {
            priority: -1000, // a low number so this directive loads after all other directives have loaded. 
            restrict: "A", // attribute only
            link: function($scope, $element, $attributes) {
                console.log(" -- Element ready!");
                // do what you want here.
            }
        };
    }]);

Teraz możesz umieścić dyrektywę ngElementReady w głównym elemencie aplikacji, a console.logbędzie uruchomiona po załadowaniu:

<body data-ng-app="MyApp" data-ng-element-ready="">
   ...
   ...
</body>

To takie proste! Po prostu utwórz prostą dyrektywę i użyj jej. ;)

Możesz go dodatkowo dostosować, aby mógł wykonać wyrażenie (tj. Funkcję), dodając $scope.$eval($attributes.ngElementReady);do niego:

    app.directive('ngElementReady', [function() {
        return {
            priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
            restrict: "A",
            link: function($scope, $element, $attributes) {
                $scope.$eval($attributes.ngElementReady); // execute the expression in the attribute.
            }
        };
    }]);

Następnie możesz go użyć na dowolnym elemencie:

<body data-ng-app="MyApp" data-ng-controller="BodyCtrl" data-ng-element-ready="bodyIsReady()">
    ...
    <div data-ng-element-ready="divIsReady()">...<div>
</body>

Po prostu upewnij się, że masz zdefiniowane funkcje (np. BodyIsReady i divIsReady) w zakresie (w kontrolerze), w którym żyje Twój element.

Ostrzeżenia: powiedziałem, że to zadziała w większości przypadków. Zachowaj ostrożność podczas korzystania z niektórych dyrektyw, takich jak ngRepeat i ngIf. Tworzą swój własny zakres, a Twoja dyrektywa może się nie uruchomić. Na przykład, jeśli umieścisz naszą nową dyrektywę ngElementReady na elemencie, który również ma ngIf, a stan ngIf zostanie oceniony jako false, wówczas nasza dyrektywa ngElementReady nie zostanie załadowana. Lub, na przykład, jeśli umieścisz naszą nową dyrektywę ngElementReady na elemencie, który również ma dyrektywę ngInclude, nasza dyrektywa nie zostanie załadowana, jeśli szablon dla ngInclude nie istnieje. Możesz obejść niektóre z tych problemów, upewniając się, że dyrektywy zostały zagnieżdżone zamiast umieszczać je wszystkie w tym samym elemencie. Na przykład robiąc to:

<div data-ng-element-ready="divIsReady()">
    <div data-ng-include="non-existent-template.html"></div>
<div>

zamiast tego:

<div data-ng-element-ready="divIsReady()" data-ng-include="non-existent-template.html"></div>

Dyrektywa ngElementReady zostanie skompilowana w drugim przykładzie, ale jej funkcja link nie zostanie wykonana. Uwaga: dyrektywy są zawsze kompilowane, ale ich funkcje łączące nie zawsze są wykonywane w zależności od pewnych scenariuszy, takich jak powyższy.

EDYTUJ, kilka minut później:

Aha, i aby w pełni odpowiedzieć na pytanie, możesz teraz $emitlub $broadcastswoje zdarzenie z wyrażenia lub funkcji, która jest wykonywana w ng-element-readyatrybucie. :) Np .:

<div data-ng-element-ready="$emit('someEvent')">
    ...
<div>

EDYTUJ, jeszcze więcej kilka minut później:

Odpowiedź @ satchmorun również działa, ale tylko podczas początkowego ładowania. Oto bardzo przydatne pytanie SO, które opisuje kolejność wykonywanych czynności, w tym funkcje linków app.runi inne. Tak więc, w zależności od twojego przypadku użycia, app.runmoże być dobre, ale nie dla określonych elementów, w takim przypadku funkcje linków są lepsze.

EDYCJA, pięć miesięcy później, 17 października o 8:11 czasu PST:

Nie działa to z podrzędnymi, które są ładowane asynchronicznie. Będziesz musiał dodać księgowość do swoich podrzędnych (np. Jednym ze sposobów jest śledzenie każdego fragmentu, kiedy jego zawartość jest ładowana, a następnie emitowanie zdarzenia, aby zakres nadrzędny mógł policzyć, ile części zostało załadowanych i na koniec zrobić to, co musi zrobić po załadowaniu wszystkich części).

EDYCJA, 23 października o 22:52 czasu PST:

Zrobiłem prostą dyrektywę do odpalania kodu, gdy ładowany jest obraz:

/*
 * This img directive makes it so that if you put a loaded="" attribute on any
 * img element in your app, the expression of that attribute will be evaluated
 * after the images has finished loading. Use this to, for example, remove
 * loading animations after images have finished loading.
 */
  app.directive('img', function() {
    return {
      restrict: 'E',
      link: function($scope, $element, $attributes) {
        $element.bind('load', function() {
          if ($attributes.loaded) {
            $scope.$eval($attributes.loaded);
          }
        });
      }
    };
  });

EDIT, 24 października o 00:48 czasu PST:

Poprawiłem moją pierwotną ngElementReadydyrektywę i zmieniłem jej nazwę na whenReady.

/*
 * The whenReady directive allows you to execute the content of a when-ready
 * attribute after the element is ready (i.e. done loading all sub directives and DOM
 * content except for things that load asynchronously like partials and images).
 *
 * Execute multiple expressions by delimiting them with a semi-colon. If there
 * is more than one expression, and the last expression evaluates to true, then
 * all expressions prior will be evaluated after all text nodes in the element
 * have been interpolated (i.e. {{placeholders}} replaced with actual values). 
 *
 * Caveats: if other directives exists on the same element as this directive
 * and destroy the element thus preventing other directives from loading, using
 * this directive won't work. The optimal way to use this is to put this
 * directive on an outer element.
 */
app.directive('whenReady', ['$interpolate', function($interpolate) {
  return {
    restrict: 'A',
    priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
    link: function($scope, $element, $attributes) {
      var expressions = $attributes.whenReady.split(';');
      var waitForInterpolation = false;

      function evalExpressions(expressions) {
        expressions.forEach(function(expression) {
          $scope.$eval(expression);
        });
      }

      if ($attributes.whenReady.trim().length == 0) { return; }

      if (expressions.length > 1) {
        if ($scope.$eval(expressions.pop())) {
          waitForInterpolation = true;
        }
      }

      if (waitForInterpolation) {
        requestAnimationFrame(function checkIfInterpolated() {
          if ($element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
            requestAnimationFrame(checkIfInterpolated);
          }
          else {
            evalExpressions(expressions);
          }
        });
      }
      else {
        evalExpressions(expressions);
      }
    }
  }
}]);

Na przykład użyj tego w ten sposób, aby uruchomić, someFunctiongdy element jest załadowany i {{placeholders}}jeszcze nie zastąpiony:

<div when-ready="someFunction()">
  <span ng-repeat="item in items">{{item.property}}</span>
</div>

someFunction zostanie wywołany przed wszystkimi item.property zastąpieniem symboli zastępczych.

Oceń tyle wyrażeń, ile chcesz, i spraw, by ostatnie wyrażenie trueczekało na {{placeholders}}ocenę w następujący sposób:

<div when-ready="someFunction(); anotherFunction(); true">
  <span ng-repeat="item in items">{{item.property}}</span>
</div>

someFunctioni anotherFunctionzostanie zwolniony po {{placeholders}}wymianie.

Działa to tylko przy pierwszym ładowaniu elementu, a nie przy przyszłych zmianach. Może nie działać zgodnie z oczekiwaniami, jeśli $digestpo początkowym zastąpieniu symboli zastępczych zachodzi ciągłość (podsumowanie $ może nastąpić do 10 razy, aż dane przestaną się zmieniać). Będzie odpowiedni w większości przypadków użycia.

EDYCJA, 31 października o 19:26 czasu PST:

W porządku, to prawdopodobnie moja ostatnia i ostatnia aktualizacja. Prawdopodobnie zadziała to dla 99,999 przypadków użycia:

/*
 * The whenReady directive allows you to execute the content of a when-ready
 * attribute after the element is ready (i.e. when it's done loading all sub directives and DOM
 * content). See: /programming/14968690/sending-event-when-angular-js-finished-loading
 *
 * Execute multiple expressions in the when-ready attribute by delimiting them
 * with a semi-colon. when-ready="doThis(); doThat()"
 *
 * Optional: If the value of a wait-for-interpolation attribute on the
 * element evaluates to true, then the expressions in when-ready will be
 * evaluated after all text nodes in the element have been interpolated (i.e.
 * {{placeholders}} have been replaced with actual values).
 *
 * Optional: Use a ready-check attribute to write an expression that
 * specifies what condition is true at any given moment in time when the
 * element is ready. The expression will be evaluated repeatedly until the
 * condition is finally true. The expression is executed with
 * requestAnimationFrame so that it fires at a moment when it is least likely
 * to block rendering of the page.
 *
 * If wait-for-interpolation and ready-check are both supplied, then the
 * when-ready expressions will fire after interpolation is done *and* after
 * the ready-check condition evaluates to true.
 *
 * Caveats: if other directives exists on the same element as this directive
 * and destroy the element thus preventing other directives from loading, using
 * this directive won't work. The optimal way to use this is to put this
 * directive on an outer element.
 */
app.directive('whenReady', ['$interpolate', function($interpolate) {
  return {
    restrict: 'A',
    priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
    link: function($scope, $element, $attributes) {
      var expressions = $attributes.whenReady.split(';');
      var waitForInterpolation = false;
      var hasReadyCheckExpression = false;

      function evalExpressions(expressions) {
        expressions.forEach(function(expression) {
          $scope.$eval(expression);
        });
      }

      if ($attributes.whenReady.trim().length === 0) { return; }

    if ($attributes.waitForInterpolation && $scope.$eval($attributes.waitForInterpolation)) {
        waitForInterpolation = true;
    }

      if ($attributes.readyCheck) {
        hasReadyCheckExpression = true;
      }

      if (waitForInterpolation || hasReadyCheckExpression) {
        requestAnimationFrame(function checkIfReady() {
          var isInterpolated = false;
          var isReadyCheckTrue = false;

          if (waitForInterpolation && $element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
            isInterpolated = false;
          }
          else {
            isInterpolated = true;
          }

          if (hasReadyCheckExpression && !$scope.$eval($attributes.readyCheck)) { // if the ready check expression returns false
            isReadyCheckTrue = false;
          }
          else {
            isReadyCheckTrue = true;
          }

          if (isInterpolated && isReadyCheckTrue) { evalExpressions(expressions); }
          else { requestAnimationFrame(checkIfReady); }

        });
      }
      else {
        evalExpressions(expressions);
      }
    }
  };
}]);

Użyj tego w ten sposób

<div when-ready="isReady()" ready-check="checkIfReady()" wait-for-interpolation="true">
   isReady will fire when this {{placeholder}} has been evaluated
   and when checkIfReady finally returns true. checkIfReady might
   contain code like `$('.some-element').length`.
</div>

Oczywiście można to prawdopodobnie zoptymalizować, ale na tym poprzestanę. requestAnimationFrame jest fajne.

trusktr
źródło
3
Naprawdę denerwujące z tymi wszystkimi prefiksami „data-”. Cieszę się, że sam ich nie używam.
stolsvik
1
@stolsvik heh, yeah, w większości nowoczesnych przeglądarek nie są potrzebne.
trusktr
49
Zasługuje na głosowanie za czas i wysiłek włożony w tę odpowiedź. Dobra robota!
GordyD
8
Dobra odpowiedź, ale rozważ usunięcie wszystkich wierszy „Edytuj” i trochę zmodyfikowanie odpowiedzi. Historia zmian jest dostępna za pośrednictwem łącza „edytowano ...” u dołu odpowiedzi i rozprasza uwagę podczas czytania.
user247702
2
Kod źródłowy byłby naprawdę pomocny. A jeśli możesz upublicznić to na npm, byłoby to IDEALNE. Naprawdę fajna odpowiedź, naprawdę ładnie wyjaśniona, +1 za włożony w to wysiłek.
tfrascaroli
38

W dokumentacji dlaangular.Module znajduje się wpis opisujący runfunkcję:

Użyj tej metody, aby zarejestrować pracę, która powinna zostać wykonana, gdy wtryskiwacz zakończy ładowanie wszystkich modułów.

Więc jeśli masz jakiś moduł, który jest Twoją aplikacją:

var app = angular.module('app', [/* module dependencies */]);

Możesz uruchamiać rzeczy po załadowaniu modułów:

app.run(function() {
  // Do post-load initialization stuff here
});

EDYCJA: Ręczna inicjalizacja na ratunek

Więc zostało powiedziane, że runnie jest wywoływany, gdy DOM jest gotowy i połączony. Jest wywoływana, gdy $injectormoduł, do którego się odwołuje ng-app, załadował wszystkie swoje zależności, co jest niezależne od kroku kompilacji DOM.

Jeszcze raz przyjrzałem się ręcznej inicjalizacji i wydaje się, że to powinno załatwić sprawę.

Zrobiłem skrzypce, żeby zilustrować .

HTML jest prosty:

<html>
    <body>
        <test-directive>This is a test</test-directive>
    </body>
</html>

Zwróć uwagę na brak ng-app . I mam dyrektywę, która zrobi pewne manipulacje DOM, więc możemy upewnić się co do kolejności i czasu rzeczy.

Jak zwykle tworzony jest moduł:

var app = angular.module('app', []);

A oto dyrektywa:

app.directive('testDirective', function() {
    return {
        restrict: 'E',
        template: '<div class="test-directive"><h1><div ng-transclude></div></h1></div>',
        replace: true,
        transclude: true,
        compile: function() {
            console.log("Compiling test-directive");
            return {
                pre: function() { console.log("Prelink"); },
                post: function() { console.log("Postlink"); }
            };
        }
    };
});

Zamierzamy zamienić test-directivetag na a divof class test-directivei opakować jego zawartość w h1.

Dodałem funkcję kompilacji, która zwraca funkcje przed i po łączeniu, dzięki czemu możemy zobaczyć, kiedy te rzeczy działają.

Oto reszta kodu:

// The bootstrapping process

var body = document.getElementsByTagName('body')[0];

// Check that our directive hasn't been compiled

function howmany(classname) {
    return document.getElementsByClassName(classname).length;
}

Zanim cokolwiek zrobimy, test-directivew DOM nie powinno być elementów z klasą , a po zakończeniu powinno być 1.

console.log('before (should be 0):', howmany('test-directive'));

angular.element(document).ready(function() {
    // Bootstrap the body, which loades the specified modules
    // and compiled the DOM.
    angular.bootstrap(body, ['app']);

    // Our app is loaded and the DOM is compiled
    console.log('after (should be 1):', howmany('test-directive'));
});

To całkiem proste. Gdy dokument jest gotowy, wywołaj angular.bootstrapelement główny aplikacji i tablicę nazw modułów.

W rzeczywistości, jeśli dołączysz runfunkcję do appmodułu , zobaczysz, że zostanie ona uruchomiona przed jakąkolwiek kompilacją.

Jeśli uruchomisz skrzypce i obejrzysz konsolę, zobaczysz:

before (should be 0): 0 
Compiling test-directive 
Prelink
Postlink
after (should be 1): 1 <--- success!
satchmorun
źródło
2
dzięki @satchmorun! ale run () wykonuje się przed końcem części łączącej - po prostu zweryfikowałem to za pomocą niektórych console.logs.
Lior
sam byłem zaciekawiony ... Mam dyrektywę, która odpala w celu zaimplementowania niektórych wtyczek jQuery DOM, runodpala przed tą dyrektywą i po uruchomieniu odpala, html nie jest tam wszystko
charlietfl
@charlietfl - zagłębiłem się trochę w ręczne uruchamianie i jest to całkiem łatwy sposób, aby uzyskać to, czego szukało pytanie. Dodałem dość długą zmianę do mojej pierwotnej odpowiedzi.
satchmorun
5
stwierdziłem, że korzystając z $timeout( initMyPlugins,0)mojej dyrektywy, cały
kod
@satchmorun, zobacz to uzupełnienie: stackoverflow.com/questions/14989161/…
Lior
16

Angular nie dostarczył sposobu, aby zasygnalizować zakończenie ładowania strony, być może dlatego, że „ukończona” zależy od aplikacji . Na przykład, jeśli masz hierarchiczne drzewo części składowych, jedna ładuje inne. „Zakończ” oznaczałoby, że wszystkie zostały załadowane. Każdy framework miałby trudności z analizą twojego kodu i zrozumieniem, że wszystko jest zrobione lub nadal czeka. W tym celu należy zapewnić logikę specyficzną dla aplikacji, aby to sprawdzić i określić.

Lior
źródło
15

Wymyśliłem rozwiązanie, które jest stosunkowo dokładne w ocenie, kiedy inicjalizacja kątowa jest zakończona.

Dyrektywa jest następująca:

.directive('initialisation',['$rootScope',function($rootScope) {
            return {
                restrict: 'A',
                link: function($scope) {
                    var to;
                    var listener = $scope.$watch(function() {
                        clearTimeout(to);
                        to = setTimeout(function () {
                            console.log('initialised');
                            listener();
                            $rootScope.$broadcast('initialised');
                        }, 50);
                    });
                }
            };
        }]);

Można to po prostu dodać jako atrybut do bodyelementu, a następnie nasłuchiwać użycia$scope.$on('initialised', fn)

Działa przy założeniu, że aplikacja jest inicjowana, gdy nie ma więcej cykli $ digest. $ watch jest wywoływana każdym cyklem podsumowania, więc uruchamiany jest licznik czasu (setTimeout nie $ timeout, więc nowy cykl podsumowania nie jest wyzwalany). Jeśli cykl podsumowania nie nastąpi w określonym czasie, zakłada się, że aplikacja została zainicjowana.

To oczywiście nie jest tak dokładne jak rozwiązanie satchmoruns (ponieważ możliwe jest, że cykl podsumowania trwa dłużej niż limit czasu), ale moje rozwiązanie nie wymaga śledzenia modułów, co znacznie ułatwia zarządzanie (szczególnie w przypadku większych projektów ). W każdym razie wydaje się być wystarczająco dokładny dla moich wymagań. Mam nadzieję, że to pomoże.

Theo
źródło
Doskonałe rozwiązanie. W przypadku projektów, w których cały kod w jednym lub dwóch skompresowanych plikach działa bardzo dobrze.
merqlove
1
To fantastyczne rozwiązanie. Jeśli masz dużo kodu w jquery i próbujesz przekształcić kod krok po kroku na kątowy, ma to sens.
Mangesh Pimpalkar,
11

Jeśli używasz Angular UI Router , możesz nasłuchiwać $viewContentLoadedzdarzenia.

„$ viewContentLoaded - uruchamiane po załadowaniu widoku, po wyrenderowaniu modelu DOM . '$ scope' widoku emituje zdarzenie”. - Link

$scope.$on('$viewContentLoaded', 
function(event){ ... });
Jordan Skole
źródło
3
$ scope. $ watch ('$ viewContentLoaded', function () zrobił dla mnie sztuczkę
Ludwik XIV
2
odrzucone dla „którym powinieneś być”. A co jeśli powiedziałbym „jeśli używasz React zamiast Angular (którym powinieneś być)…”? Niezbyt dobre podejście do tego ekosystemu IMHO.
Valentin Waeselynck
@ValentinWaeselynck masz całkowitą rację. Zredagowałem odpowiedź, aby usunąć moje uprzedzenie.
Jordan Skole,
1
Pracował dla mnie! Dziękuję Ci. Właściwie dodałem go do mojej funkcji uruchamiania, a następnie zmieniłem $ scope na $ rootScope.
JDavies
2
Jak wskazał Angular University w innej odpowiedzi, $ viewContentLoaded mógł tam nie być pierwotnie, ale teraz działa we wbudowanym dostawcy ngRoute, dokładnie w ten sam sposób. Mając to na uwadze, myślę, że jest to szybka, prosta i czytelna odpowiedź, której wielu (większości?) Przyszłych czytelników będzie szukać.
Kevin Crumley
3

obserwuję manipulację DOM na kątach za pomocą JQuery i ustawiłem wykończenie mojej aplikacji (pewna predefiniowana i zadowalająca sytuacja, której potrzebuję dla mojej aplikacji), na przykład oczekuję, że mój repeater ng da wynik 7 i tam dla mnie w tym celu ustawi funkcję obserwacyjną z pomocą setInterval.

$(document).ready(function(){

  var interval = setInterval(function(){

  if($("article").size() == 7){
     myFunction();
     clearInterval(interval);
  }

  },50);

});
Hassan Gilak
źródło
3
Nie zrobiłbym tego. Używanie interwałów do sprawdzania, czy coś się dzieje, nie jest dobrą praktyką, nie da się skalować i istnieją inne sposoby, aby coś się wydarzyło. Timery służą do wykonywania konkretnych zadań, które muszą zostać wykonane po określonym czasie, a nie do „zgadywania”, kiedy treść lub wyniki są gotowe.
dudewad
Nie wspominając już o tym, że używanie timera jquery przeciwko platformie kątowej jest nieproduktywne - angular ma klasę limitu czasu i powinieneś użyć tego, w przeciwnym razie będziesz siedzieć okrakiem na dwóch frameworkach i bardzo szybko stanie się to mylące.
dudewad
3

Jeśli nie używasz modułu ngRoute , czyli nie masz zdarzenia $ viewContentLoaded .

Możesz użyć innej metody dyrektywy:

    angular.module('someModule')
        .directive('someDirective', someDirective);

    someDirective.$inject = ['$rootScope', '$timeout']; //Inject services

    function someDirective($rootScope, $timeout){
        return {
            restrict: "A",
            priority: Number.MIN_SAFE_INTEGER, //Lowest priority
            link    : function(scope, element, attr){
                $timeout(
                    function(){
                        $rootScope.$emit("Some:event");
                    }
                );
            }
        };
    }

Zgodnie z odpowiedzią Trusktr ma najniższy priorytet. Dodatkowo $ timeout spowoduje, że Angular przejdzie przez całą pętlę zdarzeń przed wykonaniem wywołania zwrotnego.

Zastosowano $ rootScope , ponieważ pozwala na umieszczenie dyrektywy w dowolnym zakresie aplikacji i powiadomienie tylko niezbędnych słuchaczy.

$ rootScope. $ emit uruchomi zdarzenie dla wszystkich $ rootScope. $ tylko na słuchaczach. Interesujące jest to, że $ rootScope. $ Broadcast powiadomi wszystkie $ rootScope. $ On, a także $ scope. $ On słuchaczy Źródło

Vadim P.
źródło
2

Według zespołu Angular i tego wydania Github :

mamy teraz zdarzenia $ viewContentLoaded i $ includeContentLoaded, które są emitowane odpowiednio w ng-view i ng-include. Myślę, że jest to tak blisko, jak tylko można się dowiedzieć, kiedy skończymy kompilację.

Na tej podstawie wydaje się, że obecnie nie jest to możliwe do wykonania w wiarygodny sposób, w przeciwnym razie Angular dostarczyłby wydarzenie po wyjęciu z pudełka.

Uruchomienie aplikacji oznacza uruchomienie cyklu podsumowania w zakresie głównym, a także nie ma zdarzenia zakończenia cyklu podsumowania.

Zgodnie z dokumentacją projektową Angular 2 :

Ze względu na wielokrotne analizy niemożliwe jest ustalenie i powiadomienie komponentu, że model jest stabilny. Dzieje się tak, ponieważ powiadomienie może dodatkowo zmienić dane, co może spowodować ponowne uruchomienie procesu wiązania.

W związku z tym fakt, że nie jest to możliwe, jest jednym z powodów, dla których podjęto decyzję o przepisaniu w Angular 2.

Uniwersytet Angular
źródło
2

Miałem fragment, który był ładowany po / przez główną część, która przyszła przez routing.

Musiałem uruchomić funkcję po załadowaniu tej częściowej i nie chciałem pisać nowej dyrektywy i doszedłem do wniosku, że możesz użyć bezczelnego ngIf

Kontroler nadrzędnej części:

$scope.subIsLoaded = function() { /*do stuff*/; return true; };

HTML z subpartial

<element ng-if="subIsLoaded()"><!-- more html --></element>
Hashbrown
źródło
1

Jeśli chcesz wygenerować JS z danymi po stronie serwera (JSP, PHP), możesz dodać swoją logikę do usługi, która zostanie załadowana automatycznie po załadowaniu kontrolera.

Dodatkowo, jeśli chcesz reagować, gdy wszystkie dyrektywy są już kompilowane / linkowane, możesz dodać odpowiednie proponowane rozwiązania powyżej w logice inicjalizacji.

module.factory('YourControllerInitService', function() {

    // add your initialization logic here

    // return empty service, because it will not be used
    return {};
});


module.controller('YourController', function (YourControllerInitService) {
});
Nikolay Georgiev
źródło
0

To wszystko są świetne rozwiązania, jednak jeśli obecnie używasz routingu, to okazało się, że jest to najłatwiejsze i najmniej wymagające kodu. Korzystanie z właściwości „rozstrzygnij”, aby czekać na wypełnienie obietnicy przed uruchomieniem trasy. na przykład

$routeProvider
.when("/news", {
    templateUrl: "newsView.html",
    controller: "newsController",
    resolve: {
        message: function(messageService){
            return messageService.getMessage();
    }
}

})

Kliknij tutaj, aby zobaczyć pełną dokumentację - Credit to K. Scott Allen

aholtry
źródło
0

być może mogę ci pomóc na tym przykładzie

W niestandardowym fancybox pokazuję zawartość z interpolowanymi wartościami.

w serwisie, w metodzie fancybox "open" tak

open: function(html, $compile) {
        var el = angular.element(html);
     var compiledEl = $compile(el);
        $.fancybox.open(el); 
      }

$ compile zwraca skompilowane dane. możesz sprawdzić skompilowane dane

shailendra pathak
źródło