Ereignis senden, wenn AngularJS das Laden beendet hat

114

Ich habe mich gefragt, was der beste Weg ist, um das Ende des Ladens / Bootstrappens von Seiten zu erkennen, wenn alle Anweisungen kompiliert / verknüpft sind.

Gibt es schon eine Veranstaltung? Sollte ich die Bootstrap-Funktion überladen?

Lior
quelle

Antworten:

204

Nur eine Vermutung: Warum nicht schauen, wie die ngCloak-Direktive das macht? Es ist klar, dass die ngCloak-Direktive es schafft, Inhalte anzuzeigen, nachdem Dinge geladen wurden. Ich wette, ein Blick auf ngCloak wird zur genauen Antwort führen ...

EDIT 1 Stunde später: Ok, nun, ich habe mir ngCloak angesehen und es ist wirklich kurz. Dies impliziert offensichtlich, dass die Kompilierungsfunktion erst ausgeführt wird, wenn {{template}} -Ausdrücke ausgewertet wurden (dh die geladene Vorlage), was die nette Funktionalität der ngCloak-Direktive darstellt.

Meine Vermutung wäre, einfach eine Direktive mit der gleichen Einfachheit wie ngCloak zu erstellen und dann in Ihrer Kompilierungsfunktion zu tun, was Sie wollen. :) Platziere die Direktive auf dem Root-Element deiner App. Sie können die Direktive so etwas wie myOnload aufrufen und als Attribut my-onload verwenden. Die Kompilierungsfunktion wird ausgeführt, sobald die Vorlage kompiliert wurde (Ausdrücke ausgewertet und Untervorlagen geladen).

EDIT, 23 Stunden später: Ok, also habe ich einige Nachforschungen angestellt und auch meine eigene Frage gestellt . Die Frage, die ich gestellt habe, war indirekt mit dieser Frage verbunden, führte mich aber zufällig zu der Antwort, die diese Frage löst.

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 unten erläuterten Anwendungsfälle) ausgeführt wird, wenn Ihr Element bereit / geladen ist. Basierend auf Joshs Beschreibung der Reihenfolge, in der Kompilierungs- und Verknüpfungsfunktionen ausgeführt werden ,

Wenn Sie dieses Markup haben:

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

Dann erstellt AngularJS die Direktiven, indem Direktivenfunktionen in einer bestimmten Reihenfolge ausgeführt werden:

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

Standardmäßig ist eine gerade "Link" -Funktion eine Post-Link-Funktion, sodass die Link-Funktion Ihrer äußeren Direktive1 erst ausgeführt wird, nachdem die Link-Funktion der inneren Direktive2 ausgeführt wurde. Deshalb sagen wir, dass es nur sicher ist, DOM-Manipulationen im Post-Link durchzuführen. In Bezug auf die ursprüngliche Frage sollte es also kein Problem geben, über die Link-Funktion der äußeren Direktive auf das innere HTML der untergeordneten Direktive zuzugreifen, obwohl dynamisch eingefügte Inhalte wie oben beschrieben kompiliert werden müssen.

Daraus können wir schließen, dass wir einfach eine Anweisung zur Ausführung unseres Codes erstellen können, wenn alles fertig / kompiliert / verknüpft / geladen ist:

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

Jetzt können Sie die Direktive ngElementReady auf das Stammelement der App setzen, und der console.logWille wird ausgelöst, wenn sie geladen wird:

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

So einfach ist das! Machen Sie einfach eine einfache Anweisung und verwenden Sie sie. ;)

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

    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:

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

Stellen Sie einfach sicher, dass Ihre Funktionen (z. B. bodyIsReady und divIsReady) in dem Bereich (im Controller) definiert sind, unter dem Ihr Element lebt.

Vorsichtsmaßnahmen: Ich sagte, dass dies in den meisten Fällen funktionieren wird. Seien Sie vorsichtig, wenn Sie bestimmte Anweisungen wie ngRepeat und ngIf verwenden. Sie erstellen ihren eigenen Bereich, und Ihre Direktive wird möglicherweise nicht ausgelöst. Wenn Sie beispielsweise unsere neue ngElementReady-Direktive auf ein Element setzen, das auch ngIf enthält, und die Bedingung der ngIf als false ausgewertet wird, wird unsere ngElementReady-Direktive nicht geladen. Wenn Sie beispielsweise unsere neue ngElementReady-Direktive auf ein Element setzen, das auch eine ngInclude-Direktive enthält, wird unsere Direktive nicht geladen, wenn die Vorlage für ngInclude nicht vorhanden ist. Sie können einige dieser Probleme umgehen, indem Sie sicherstellen, dass Sie die Anweisungen verschachteln, anstatt sie alle auf dasselbe Element zu setzen. Zum Beispiel auf diese Weise:

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

an Stelle von:

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

Die Anweisung ngElementReady wird im letzteren Beispiel kompiliert, die Verknüpfungsfunktion wird jedoch nicht ausgeführt. Hinweis: Direktiven werden immer kompiliert, aber ihre Verknüpfungsfunktionen werden abhängig von bestimmten Szenarien wie den oben genannten nicht immer ausgeführt.

BEARBEITEN, einige Minuten später:

Oh, und um die Frage vollständig zu beantworten, können Sie jetzt $emitoder $broadcastIhr Ereignis aus dem Ausdruck oder der Funktion, die im ng-element-readyAttribut ausgeführt wird. :) Z.B:

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

EDIT, noch einige Minuten später:

Die Antwort von @ satchmorun funktioniert auch, aber nur für das anfängliche Laden. Hier ist eine sehr nützliche SO-Frage , die die Reihenfolge beschreibt, in der Dinge ausgeführt werden, einschließlich Verknüpfungsfunktionen app.runund anderer. Abhängig von Ihrem Anwendungsfall ist dies app.runmöglicherweise gut, jedoch 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 Ihren Partials Buchhaltung hinzufügen (z. B. besteht eine Möglichkeit darin, dass jeder Teil nachverfolgt wird, wann der Inhalt geladen ist, und dann ein Ereignis ausgibt, damit der übergeordnete Bereich zählen kann, wie viele Partials geladen wurden, und schließlich das tut, was er benötigt tun, nachdem alle Partials geladen wurden).

EDIT, 23. Oktober um 22.52 Uhr PST:

Ich habe eine einfache Anweisung zum Auslösen von Code beim Laden eines Bildes erstellt:

/*
 * 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 00:48 Uhr PST:

Ich habe meine ursprüngliche ngElementReadyRichtlinie verbessert und in umbenannt 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);
      }
    }
  }
}]);

Verwenden Sie es beispielsweise so, um zu feuern, someFunctionwenn ein Element geladen und {{placeholders}}noch nicht ersetzt wird:

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

someFunctionwird aufgerufen, bevor alle item.propertyPlatzhalter ersetzt werden.

Bewerten Sie so viele Ausdrücke, wie Sie möchten, und lassen Sie den letzten Ausdruck, trueauf den Sie warten, {{placeholders}}wie folgt ausgewertet werden:

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

someFunctionund anotherFunctionwird nach dem {{placeholders}}Austausch abgefeuert .

Dies funktioniert nur beim ersten Laden eines Elements, nicht bei zukünftigen Änderungen. Es funktioniert möglicherweise nicht wie gewünscht, wenn $digestnach dem ersten Ersetzen von Platzhaltern immer wieder ein Fehler auftritt (ein $ Digest kann bis zu 10 Mal auftreten, bis sich die Daten nicht mehr ändern). Es ist für die meisten Anwendungsfälle geeignet.

EDIT, 31. Oktober um 19:26 Uhr PST:

Okay, dies ist wahrscheinlich mein letztes und letztes Update. Dies wird wahrscheinlich für 99,999 der Anwendungsfälle funktionieren:

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

Verwenden Sie es so

<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>

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

trusktr
quelle
3
Wirklich nervig mit all diesen "Daten" -Präfixen. Ich bin froh, dass ich sie nicht selbst benutze.
Stolsvik
1
@stolsvik heh, ja, in den modernsten Browsern werden sie nicht benötigt.
Trusktr
49
Verdient eine Abstimmung über die Zeit und Mühe, die in diese Antwort gesteckt werden. Gute Arbeit!
GordyD
8
Schöne Antwort, aber bitte entfernen Sie alle "Bearbeiten" -Zeilen und strukturieren Sie Ihre Antwort ein wenig um. Der Bearbeitungsverlauf ist über den Link "Bearbeitet ..." am Ende Ihrer Antwort verfügbar und lenkt beim Lesen ab.
user247702
2
Der Quellcode wäre wirklich hilfreich. Und wenn Sie es auf npm veröffentlichen können, wäre das PERFEKT. Wirklich nette Antwort, wirklich nett erklärt, +1 für den Aufwand, der in diese Sache gesteckt wird.
Tfrascaroli
38

In den Dokumenten fürangular.Module gibt es einen Eintrag, der die runFunktion beschreibt:

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

Wenn Sie also ein Modul haben, das Ihre App ist:

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

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

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

EDIT: Manuelle Initialisierung zur Rettung

Es wurde also darauf hingewiesen, dass das runnicht aufgerufen wird, wenn das DOM bereit und verbunden ist. Es wird aufgerufen, wenn das $injectorfür das Modul, auf das verwiesen ng-appwird, alle seine Abhängigkeiten geladen hat, was vom DOM-Kompilierungsschritt getrennt ist.

Ich habe mir die manuelle Initialisierung noch einmal angesehen , und es scheint, dass dies den Trick machen sollte.

Ich habe eine Geige gemacht, um zu veranschaulichen .

Der HTML-Code ist einfach:

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

Beachten Sie das Fehlen eines ng-app. Und ich habe eine Anweisung, die einige DOM-Manipulationen vornimmt, damit wir die Reihenfolge und das Timing der Dinge sicherstellen 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: '<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"); }
            };
        }
    };
});

Wir werden das test-directiveTag durch ein divof-Klasse ersetzen test-directiveund seinen Inhalt in ein verpacken h1.

Ich habe eine Kompilierungsfunktion hinzugefügt, die sowohl Pre- als auch Post-Link-Funktionen zurückgibt, damit wir sehen können, wann diese Dinge ausgeführt werden.

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 Klasse von test-directiveim DOM geben, und nachdem wir fertig sind, sollte es 1 geben.

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.bootstrapmit dem Stammelement Ihrer App und einer Reihe von Modulnamen auf.

Wenn Sie rundem appModul eine Funktion hinzufügen , wird diese tatsächlich ausgeführt, bevor die Kompilierung stattfindet.

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

before (should be 0): 0 
Compiling test-directive 
Prelink
Postlink
after (should be 1): 1 <--- success!
satchmorun
quelle
2
danke @satchmorun! run () wird jedoch vor dem Ende des Verknüpfungsteils ausgeführt - es wurde nur mit einigen console.logs überprüft.
Lior
war selbst neugierig ... Ich habe eine Direktive, die ausgelöst wird, um einige jQuery DOM-Plugins zu implementieren, rundie vor der Direktive
ausgelöst werden,
@charlietfl - Ich habe mich ein wenig mit manuellem Bootstrapping beschäftigt, und es ist eigentlich ein ziemlich einfacher Weg, um herauszufinden, wonach die Frage sucht. Ich habe meiner ursprünglichen Antwort eine ziemlich lange Bearbeitung hinzugefügt.
Satchmorun
5
Ich fand, dass mit $timeout( initMyPlugins,0)Arbeiten innerhalb meiner Direktive alles HTML, das ich brauche, da ist
charlietfl
@satchmorun, siehe dieses Follow-up: stackoverflow.com/questions/14989161/…
Lior
16

Angular hat keine Möglichkeit bereitgestellt, zu signalisieren, wann eine Seite vollständig geladen wurde, möglicherweise weil "fertig" von Ihrer Anwendung abhängt . Wenn Sie beispielsweise einen hierarchischen Teilbaum haben, lädt einer den anderen. "Fertig stellen" 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. Dazu müssten Sie eine anwendungsspezifische Logik bereitstellen, um dies zu überprüfen und festzustellen.

Lior
quelle
14

Ich habe eine Lösung gefunden, die relativ genau bewertet, wann die Winkelinitialisierung abgeschlossen 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);
                    });
                }
            };
        }]);

Das kann dann einfach als Attribut zum bodyElement hinzugefügt und dann auf Verwendung abgehört werden$scope.$on('initialised', fn)

Es wird davon ausgegangen, dass die Anwendung initialisiert wird, wenn keine $ Digest-Zyklen mehr vorhanden sind. $ watch wird bei jedem Digest-Zyklus aufgerufen und daher wird ein Timer gestartet (setTimeout nicht $ timeout, damit kein neuer Digest-Zyklus ausgelöst wird). Wenn innerhalb des Timeouts kein Digest-Zyklus auftritt, wird davon ausgegangen, dass die Anwendung initialisiert wurde.

Es ist offensichtlich nicht so genau wie die Satchmoruns-Lösung (da ein Digest-Zyklus möglicherweise länger dauert als das Timeout), aber bei meiner Lösung müssen Sie nicht die Module im Auge behalten, was die Verwaltung erheblich vereinfacht (insbesondere bei größeren Projekten) ). Wie auch immer, scheint genau genug für meine Anforderungen zu sein. Ich hoffe es hilft.

Das Ö
quelle
Hervorragende Lösung. Für Projekte, bei denen der gesamte Code in einer oder zwei komprimierten Dateien sehr gut funktioniert.
Merqlove
1
Dies ist eine fantastische Lösung. Wenn Sie viel Code in jquery haben und versuchen, den Code Schritt für Schritt in Winkel umzuwandeln, ist dies absolut sinnvoll.
Mangesh Pimpalkar
11

Wenn Sie Angular UI Router verwenden , können Sie auf das $viewContentLoadedEreignis warten.

"$ viewContentLoaded - wird ausgelöst, sobald die Ansicht geladen wurde, nachdem das DOM gerendert wurde . Der '$ scope' der Ansicht gibt das Ereignis aus." - Link

$scope.$on('$viewContentLoaded', 
function(event){ ... });
Jordan Skole
quelle
3
$ scope. $ watch ('$ viewContentLoaded', function () machte den Trick für mich
Louis XIV
2
für das "was du sein solltest" herabgestimmt. Was ist, wenn ich sagte "Wenn Sie React anstelle von Angular verwenden (was Sie sein sollten) ..."? Keine große Einstellung in diesem Ökosystem IMHO zu haben.
Valentin Waeselynck
@ ValentinWaeselynck du hast absolut recht. Ich habe meine Antwort bearbeitet, um meine Vorurteile zu beseitigen.
Jordan Skole
1
Hat für mich gearbeitet! Danke dir. Ich habe es tatsächlich zu meiner Ausführungsfunktion hinzugefügt und dann $ scope in $ rootScope geändert.
JDavies
2
Wie die Angular University in einer anderen Antwort hervorhob, war $ viewContentLoaded möglicherweise ursprünglich nicht vorhanden, funktioniert jetzt jedoch im integrierten ngRoute-Anbieter genauso. Vor diesem Hintergrund denke ich, dass dies die schnelle, einfache und lesbare Antwort ist, nach der viele (die meisten?) Zukünftigen Leser suchen werden.
Kevin Crumley
3

Ich beobachte die DOM-Manipulation des Winkels mit JQuery und habe ein Finish für meine App festgelegt (eine vordefinierte und zufriedenstellende Situation, die ich für meine App-Zusammenfassung benötige). Ich erwarte beispielsweise, dass mein ng-Repeater 7 Ergebnisse liefert und dort für i wird zu diesem Zweck mit Hilfe von setInterval eine Beobachtungsfunktion einstellen.

$(document).ready(function(){

  var interval = setInterval(function(){

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

  },50);

});
Hassan Gilak
quelle
3
Ich würde das nicht tun. Das Verwenden von Intervallen, um zu überprüfen, ob etwas passiert, ist keine gute Praxis, nicht skalierbar, und es gibt andere Möglichkeiten, um etwas zu bewirken. Timer dienen dazu, konkrete Aufgaben zu erledigen, die nach einem bestimmten Zeitraum ausgeführt werden müssen, und nicht dazu, zu "erraten", wann Inhalte oder Ergebnisse fertig sind.
Dudewad
Ganz zu schweigen davon, dass die Verwendung eines Abfrage-Timers für die Winkelplattform kontraproduktiv ist. Winkel hat eine Zeitüberschreitungsklasse. Sie sollten diese verwenden, da Sie sich sonst auf zwei Frameworks befinden und dies sehr schnell verwirrend wird.
Dudewad
3

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

Sie können eine andere Direktivenmethode 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 bewirkt, dass Angular vor der Ausführung des Rückrufs eine gesamte Ereignisschleife durchläuft.

$ rootScope wird verwendet, da damit Direktiven in einen beliebigen Bereich der Anwendung eingefügt und nur die erforderlichen Listener benachrichtigt werden können.

$ rootScope. $ emit löst ein Ereignis für alle $ rootScope. $ nur für Listener aus. Der interessante Teil ist, dass $ rootScope. $ Broadcast alle $ rootScope. $ On sowie $ scope. $ On Listener Source benachrichtigt

Vadim P.
quelle
2

Laut dem Angular-Team und dieser Github-Ausgabe :

Wir haben jetzt $ viewContentLoaded- und $ includeContentLoaded-Ereignisse, 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.

Auf dieser Grundlage scheint dies derzeit nicht zuverlässig möglich zu sein, da Angular sonst das Ereignis sofort bereitgestellt hätte.

Das Bootstrapping der App impliziert das Ausführen des Digest-Zyklus im Root-Bereich, und es gibt auch kein Ereignis, bei dem der Digest-Zyklus beendet ist.

Gemäß den Angular 2- Designdokumenten :

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

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

Angular University
quelle
2

Ich hatte ein Fragment, das nach / durch den Hauptteil geladen wurde, der über das Routing hereinkam.

Ich musste eine Funktion ausführen, nachdem dieses Subpartial geladen wurde, und ich wollte keine neue Direktive schreiben und fand heraus, dass Sie eine freche verwenden könnten ngIf

Controller des übergeordneten Teils:

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

HTML von subpartial

<element ng-if="subIsLoaded()"><!-- more html --></element>
Hashbrown
quelle
1

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 reagieren möchten, wenn alle Anweisungen fertig kompiliert / verknüpft sind, können Sie die entsprechenden Lösungsvorschläge oben 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) {
});
Nikolay Georgiev
quelle
0

Dies sind alles großartige Lösungen. Wenn Sie jedoch derzeit Routing verwenden, habe ich festgestellt, dass diese Lösung die einfachste und am wenigsten benötigte Codemenge ist. Verwenden Sie die Eigenschaft "Auflösen", um auf das Ausfüllen eines Versprechens zu warten, bevor Sie die Route auslösen. z.B

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

})

Klicken Sie hier für die vollständigen Dokumente - Dank an K. Scott Allen

aholtry
quelle
0

Vielleicht kann ich Ihnen anhand dieses Beispiels helfen

In der benutzerdefinierten Fancybox zeige ich Inhalte mit interpolierten Werten.

im Service, in der "offenen" Fancybox-Methode, mache ich

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

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

Shailendra Pathak
quelle