Sendeereignis, wenn angular.js geladen wurde

Ich habe mich gefragt, wie man das Ende des Ladens / Bootstrappings der Seite am besten erkennt, wenn alle statementen das Kompilieren / Verknüpfen abgeschlossen haben.

Irgendeine Veranstaltung schon da? Sollte ich die Bootstrap-function überlasten?

Solutions Collecting From Web of "Sendeereignis, wenn angular.js geladen wurde"

Nur eine Ahnung: Warum nicht schauen, wie die ngCloak-Richtlinie das tut? Es ist offensichtlich, dass die ngCloak-Direktive Inhalte nach dem Laden der Dinge anzeigt. Ich wette, dass ich mit ngCloak zur genauen Antwort komme …

EDIT 1 Stunde später: Ok, nun, ich habe ngCloak angeschaut und es ist wirklich kurz. Offensichtlich bedeutet dies, dass die Kompilierfunktion erst ausgeführt wird, wenn {{template}} Ausdrücke ausgewertet wurden (dh die geladene Vorlage), also die nette functionalität der ngCloak-Direktive.

Meine Vermutung wäre, einfach eine Direktive mit der gleichen Einfachheit von ngCloak zu erstellen, und dann in Ihrer Kompilierfunktion alles zu tun, was Sie tun möchten. 🙂 Platzieren Sie die statement auf dem Stammelement Ihrer App. Sie können die statement wie myOnload aufrufen und als Attribut my-onload verwenden. Die Kompilierfunktion wird ausgeführt, sobald die Vorlage kompiliert wurde (Ausdrücke ausgewertet und Untervorlagen geladen).

BEARBEITEN, 23 Stunden später: Ok, also habe ich etwas recherchiert und auch meine eigene Frage gestellt . Die Frage, die ich stellte, war indirekt mit dieser Frage verbunden, aber sie führte mich zufällig zu der Antwort, die diese Frage triggers.

Die Antwort ist, dass Sie eine einfache Direktive erstellen und Ihren Code in die Verknüpfungsfunktion der Direktive einfügen können, die (für die meisten Anwendungsfälle, die unten erklärt werden) ausgeführt wird, wenn Ihr Element bereit / geladen ist. Basierend auf Joshs Beschreibung der Reihenfolge, in der Compile- und Link-functionen ausgeführt werden ,

Wenn Sie dieses Markup haben:

Dann wird AngularJS die Direktiven erstellen, indem er direktive functionen in einer bestimmten Reihenfolge ausführt:

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

Standardmäßig ist eine direkte “Link” -function ein Post-Link, so dass die Link-function Ihrer äußeren directive1 erst ausgeführt wird, nachdem die Link-function der inneren directive2 gelaufen ist. Deshalb sagen wir, dass es nur sicher ist, DOM-Manipulation in der Post-Link zu machen. In Bezug auf die ursprüngliche Frage sollte es also kein Problem geben, von der Verknüpfungsfunktion der äußeren Direktive auf den inneren HTML-Code der Kind-Direktive zuzugreifen, obwohl dynamisch eingefügte Inhalte wie oben beschrieben kompiliert werden müssen.

Daraus können wir schließen, dass wir einfach eine Direktive machen können, um unseren Code auszuführen, wenn alles fertig ist / kompiliert / verlinkt / geladen:

  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. } }; }]); 

Nun können Sie die statement ngElementReady in das root-Element der App console.log , und die console.log wird beim Laden console.log :

  ... ...  

So einfach ist das! Machen Sie einfach eine einfache statement und verwenden Sie sie. 😉

Sie können es weiter anpassen, so dass es einen Ausdruck (dh eine function) ausführen kann, indem Sie $scope.$eval($attributes.ngElementReady); hinzufügen $scope.$eval($attributes.ngElementReady); dazu:

  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. } }; }]); 

Dann können Sie es für jedes Element verwenden:

  ... 
...

Stellen Sie nur sicher, dass Ihre functionen (z. B. bodyIsReady und divIsReady) im Gültigkeitsbereich (im Controller) definiert sind, unter dem Ihr Element lebt.

Vorbehalte: Ich sagte, das wird in den meisten Fällen funktionieren. Seien Sie vorsichtig, wenn Sie bestimmte statementen wie ngRepeat und ngIf verwenden. Sie erstellen ihren eigenen Bereich, und Ihre statement darf nicht ausgetriggers werden. Wenn Sie beispielsweise unsere neue ngElementReady-Direktive auf ein Element setzen, das ebenfalls ngIf enthält, und die Bedingung von ngIf zu false führt, wird unsere ngElementReady-Direktive nicht geladen. Oder, zum Beispiel, wenn Sie unsere neue ngElementReady-Direktive auf ein Element setzen, das auch eine ngInclude-Direktive hat, wird unsere Direktive nicht geladen, wenn die Vorlage für ngInclude nicht existiert. Sie können einige dieser Probleme umgehen, indem Sie sicherstellen, dass Sie die Direktiven verschachteln, anstatt sie alle auf dasselbe Element zu setzen. Zum Beispiel, indem Sie dies tun:

 

an Stelle von:

 

Die statement ngElementReady wird im letzten Beispiel kompiliert, aber die Link-function wird nicht ausgeführt. Hinweis: Direktiven werden immer kompiliert, aber ihre Verknüpfungsfunktionen werden nicht immer in Abhängigkeit von bestimmten Szenarien wie dem oben genannten ausgeführt.

EDIT, ein paar Minuten später:

Oh, und um die Frage vollständig zu beantworten, können Sie jetzt $emit oder $broadcast Ihr Ereignis von dem Ausdruck oder der function $broadcast , die im Attribut ng-element-ready werden. 🙂 Z.B:

 
...

BEARBEITEN, noch mehr Minuten später:

@ satchmoruns Antwort funktioniert auch, aber nur für die erste Ladung. Hier ist eine sehr nützliche SO-Frage , die die Reihenfolge beschreibt, in der Dinge ausgeführt werden, einschließlich Link-functionen, app.run und andere. Abhängig von Ihrem Anwendungsfall ist app.run möglicherweise gut, aber nicht für bestimmte Elemente. In diesem Fall sind Verknüpfungsfunktionen besser.

EDIT, fünf Monate später, 17. Oktober um 8:11 PST:

Dies funktioniert nicht mit Partials, die asynchron geladen werden. Sie müssen die Buchhaltung in Ihre Teilzahlen einfügen (zB besteht eine Möglichkeit darin, dass jeder Teil den Inhalt nach dem Laden verfolgt und dann ein Ereignis ausgibt, damit der Elternbereich zählen kann, wie viele Teiltafeln geladen wurden und schließlich tun, was er benötigt tun, nachdem alle Teiltöne geladen sind).

EDIT, 23. Oktober um 10:52 Uhr PST:

Ich habe eine einfache statement zum Auslösen von Code erstellt, wenn ein Bild geladen wird:

 /* * 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. Oktober um 12:48 Uhr PST:

Ich habe meine ursprüngliche ngElementReady Direktive verbessert und sie in whenReady .

 /* * The whenReady directive allows you to execute the content of a when-ready * attribute after the element is ready (ie 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 (ie {{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); } } } }]); 

Verwenden Sie es zum Beispiel zum someFunction wenn ein Element geladen ist und {{placeholders}} noch nicht ersetzt wurde:

 
{{item.property}}

someFunction wird aufgerufen, bevor alle item.property Platzhalter ersetzt werden.

Werten Sie so viele Ausdrücke aus, wie Sie möchten, und lassen Sie den letzten Ausdruck auf ” {{placeholders}} warten, damit er wie {{placeholders}} ausgewertet wird:

 
{{item.property}}

someFunction und anotherFunction werden ausgetriggers, nachdem {{placeholders}} ersetzt wurde.

Dies funktioniert nur beim ersten Laden eines Elements, nicht bei zukünftigen Änderungen. Es funktioniert möglicherweise nicht wie gewünscht, wenn ein $digest nach dem Ersetzen der Platzhalter immer noch stattfindet (ein $ digest kann bis zu zehn Mal vorkommen, bis sich die Daten nicht mehr ändern). Es wird für die überwiegende Mehrheit der Anwendungsfälle geeignet sein.

EDIT, 31. Oktober um 19.26 Uhr PST:

Okay, das ist wahrscheinlich mein letztes und letztes Update. Dies wird wahrscheinlich für 99.999 der Anwendungsfälle da draußen funktionieren:

 /* * The whenReady directive allows you to execute the content of a when-ready * attribute after the element is ready (ie when it's done loading all sub directives and DOM * content). See: https://stackoverflow.com/questions/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 (ie * {{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); } } }; }]); 

Benutze es so

 
isReady will fire when this {{placeholder}} has been evaluated and when checkIfReady finally returns true. checkIfReady might contain code like `$('.some-element').length`.

Natürlich kann es wahrscheinlich optimiert werden, aber ich werde es dabei belassen. requestAnimationFrame ist nett.

In den Dokumenten für angular.Module gibt es einen Eintrag, der die run function beschreibt:

Verwenden Sie diese Methode, um Arbeiten zu registrieren, die ausgeführt werden sollen, wenn der Injektor alle Module geladen hat.

Wenn du also ein Modul hast, das deine App ist:

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

Sie können die functionen ausführen, nachdem die Module geladen wurden mit:

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

EDIT: Manuelle Initialisierung zur Rettung

Es wurde darauf hingewiesen, dass der run nicht aufgerufen wird, wenn das DOM bereit und verbunden ist. Es wird aufgerufen, wenn der $injector für das von ng-app referenzierte Modul alle Abhängigkeiten geladen hat, was vom DOM-Kompilierungsschritt getrennt ist.

Ich schaute noch einmal auf die manuelle Initialisierung , und es scheint, dass dies den Trick tun sollte.

Ich habe eine Geige gemacht, um es zu illustrieren .

Der HTML-Code ist einfach:

   This is a test   

Notieren Sie das Fehlen einer ng-app . Und ich habe eine Direktive, die einige DOM-Manipulationen durchführt, damit wir uns der Reihenfolge und dem Timing der Dinge sicher sein können.

Wie üblich wird ein Modul erstellt:

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

Und hier ist die Richtlinie:

 app.directive('testDirective', function() { return { restrict: 'E', template: '

', replace: true, transclude: true, compile: function() { console.log("Compiling test-directive"); return { pre: function() { console.log("Prelink"); }, post: function() { console.log("Postlink"); } }; } }; });

Wir werden das Tag test-directive durch ein div der class test-directive ersetzen und den Inhalt in eine h1 .

Ich habe eine Kompilierfunktion hinzugefügt, die sowohl Pre- als auch Post-Link-functionen zurückgibt, damit wir sehen können, wann diese Dinge laufen.

Hier ist der Rest des Codes:

 // 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; } 

Bevor wir etwas getan haben, sollte es keine Elemente mit einer class von test-directive im DOM geben, und nachdem wir fertig sind, sollte es 1 sein.

 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')); }); 

Es ist ziemlich einfach. Wenn das Dokument fertig ist, rufen Sie angular.bootstrap mit dem angular.bootstrap Ihrer App und einem Array von angular.bootstrap .

Wenn Sie dem app Modul eine run function zuordnen , werden Sie feststellen, dass es vor dem Kompilieren ausgeführt wird.

Wenn Sie die Geige laufen lassen und die Konsole ansehen, sehen Sie Folgendes:

 before (should be 0): 0 Compiling test-directive Prelink Postlink after (should be 1): 1 < --- success! 

Wollte eine weitere Erkenntnis hinzufügen, die ich über meine eigene Frage hatte: Angular hat keine Möglichkeit angeboten, zu signalisieren, wann eine Seite fertig geladen wurde, vielleicht weil “fertig” von Ihrer Anwendung abhängt . Wenn Sie zum Beispiel eine hierarchische Struktur von Partials haben, laden Sie die anderen. “Ende” würde bedeuten, dass alle geladen wurden. Jedes Framework würde es schwer haben, Ihren Code zu analysieren und zu verstehen, dass alles erledigt ist oder noch gewartet wird. Dafür müssten Sie eine anwendungsspezifische Logik bereitstellen, um das zu überprüfen und zu bestimmen.

Vielen Dank

Lior

Ich habe eine Lösung gefunden, die bei der Auswertung der angularinitialisierung relativ genau ist.

Die Richtlinie lautet:

 .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); }); } }; }]); 

Dies kann dann einfach als Attribut zum body Element hinzugefügt und dann mit $scope.$on('initialised', fn) abgehört werden $scope.$on('initialised', fn)

Es geht davon aus, dass die Anwendung initialisiert wird, wenn es keine $ Digest-Zyklen mehr gibt. $ watch wird bei jedem Digest-Zyklus aufgerufen und somit wird ein Timer gestartet (setTimeout nicht $ timeout, so dass kein neuer Digest-Zyklus ausgetriggers wird). Wenn innerhalb des Timeouts kein Digest-Zyklus auftritt, wird angenommen, dass die Anwendung initialisiert wurde.

Es ist natürlich nicht so genau wie die satchmoruns-Lösung (da ein Digest-Zyklus länger dauert als das Timeout), aber meine Lösung erfordert nicht, dass Sie die Module im Auge behalten, was die Verwaltung wesentlich vereinfacht (besonders bei größeren Projekten) ). Wie auch immer, scheint genau genug für meine Anforderungen zu sein. Ich hoffe es hilft.

Wenn Sie den Angular UI Router verwenden , können Sie auf das $viewContentLoaded Ereignis $viewContentLoaded .

“$ viewContentLoaded – wird ausgetriggers, sobald die Ansicht geladen ist, nachdem das DOM gerendert wurde . Der ‘$ scope’ der Ansicht gibt das Ereignis aus.” – Link

 $scope.$on('$viewContentLoaded', function(event){ ... }); 

Ich beobachte DOM Manipulation von eckigen mit JQuery und ich habe ein Ziel für meine App (eine Art vordefinierte und zufriedenstellende Situation, die ich für meine app-abstract brauche) gesetzt, zum Beispiel erwarte ich, dass mein ng-Repeater 7 Ergebnis und dort für mich produzieren Zu diesem Zweck wird mit Hilfe von setInterval eine Beobachtungsfunktion gesetzt.

 $(document).ready(function(){ var interval = setInterval(function(){ if($("article").size() == 7){ myFunction(); clearInterval(interval); } },50); }); 

Wenn Sie das ngRoute- Modul nicht verwenden, dh Sie haben kein $ viewContentLoaded- Ereignis.

Sie können eine andere direktive Methode verwenden:

  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"); } ); } }; } 

Entsprechend der Antwort von trusktr hat es die niedrigste Priorität. Plus $ timeout führt dazu, dass Angular vor der Ausführung des Callbacks eine komplette Ereignisschleife durchläuft.

$ rootScope wird verwendet, weil es erlaubt, die Direktive in irgendeinen Bereich der Anwendung zu setzen und nur notwendige Listener zu benachrichtigen.

$ rootScope. $ emot triggers ein Ereignis für alle $ rootScope. $ nur für Listener aus. Der interessante Teil ist, dass $ rootScope. $ Broadcast alle $ rootScope. $ On sowie $ scope. $ On listeners Source benachrichtigt

Laut dem Angular-Team und dieser Github-Ausgabe :

Wir haben jetzt die Ereignisse $ viewContentLoaded und $ includeContentLoaded, die in ng-view bzw. ng-include ausgegeben werden. Ich denke, das ist so nah wie möglich, wenn wir mit der Zusammenstellung fertig sind.

Aus diesem Grund scheint dies derzeit nicht möglich zu sein, sonst hätte Angular das Event out of the box bereitgestellt.

Beim Bootstrapping der App wird der Digest-Zyklus im Root-Bereich ausgeführt, und es ist auch kein abgeschlossener Digest-Zyklus-Event vorhanden.

Laut der Angular 2 Design-Dokumentation :

Aufgrund mehrerer Digests ist es unmöglich, die Komponente zu bestimmen und zu benachrichtigen, dass das Modell stabil ist. Dies liegt daran, dass die Benachrichtigung die Daten weiter ändern kann, wodurch der Bindungsprozess erneut gestartet werden kann.

Daher ist die Tatsache, dass dies nicht möglich ist, einer der Gründe, warum die Entscheidung für eine Neufassung in Angular 2 getroffen wurde.

Ich hatte ein Fragment, das nach / von dem Hauptteil geladen wurde, der via Routing hereinkam.

Ich musste eine function nach dem subpartial geladen laden und ich wollte nicht eine neue Direktive schreiben und herausgefunden, dass Sie eine freche ngIf verwenden ngIf

Controller des Elternteils:

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

HTML von subpartial

  

Wenn Sie JS mit serverseitigen Daten (JSP, PHP) generieren möchten, können Sie Ihre Logik einem Dienst hinzufügen, der automatisch geladen wird, wenn Ihr Controller geladen wird.

Wenn Sie außerdem reagieren möchten, wenn alle statementen kompiliert / verknüpft sind, können Sie die entsprechenden vorgeschlagenen Lösungen in der Initialisierungslogik hinzufügen.

 module.factory('YourControllerInitService', function() { // add your initialization logic here // return empty service, because it will not be used return {}; }); module.controller('YourController', function (YourControllerInitService) { }); 

Dies sind alles großartige Lösungen. Wenn Sie jedoch derzeit Routing verwenden, dann fand ich diese Lösung am einfachsten und am wenigsten Code benötigt. Verwenden Sie die Eigenschaft ‘resolve’, um darauf zu warten, dass ein Versprechen abgeschlossen wird, bevor die Route ausgetriggers wird. z.B

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

})

Klicken Sie hier für die vollständige Dokumentation – Kredit an K. Scott Allen

vielleicht kann ich dir an diesem Beispiel weiterhelfen

In der Custom fancybox zeige ich Inhalte mit interpolierten Werten an.

im Dienst, in der “offenen” fancybox-Methode, tue ich

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

Das $ compile gibt kompilierte Daten zurück. Sie können die kompilierten Daten überprüfen