Invio dell'evento quando AngularJS ha terminato il caricamento


114

Mi chiedevo quale fosse il modo migliore per rilevare la fine del caricamento / bootstrap della pagina, quando tutte le direttive hanno completato la compilazione / il collegamento.

Qualche evento già lì? Devo sovraccaricare la funzione bootstrap?

Risposte:


204

Solo un'intuizione: perché non guardare come funziona la direttiva ngCloak? Chiaramente la direttiva ngCloak riesce a mostrare il contenuto dopo che le cose sono state caricate. Scommetto che guardare ngCloak porterà alla risposta esatta ...

EDIT 1 ora dopo: Ok, beh, ho guardato ngCloak ed è davvero breve. Ciò che questo ovviamente implica è che la funzione di compilazione non verrà eseguita fino a quando le espressioni {{template}} non saranno state valutate (cioè il modello che ha caricato), quindi la bella funzionalità della direttiva ngCloak.

La mia ipotesi plausibile sarebbe quella di creare una direttiva con la stessa semplicità di ngCloak, quindi nella tua funzione di compilazione fai quello che vuoi fare. :) Posiziona la direttiva sull'elemento radice della tua app. Puoi chiamare la direttiva qualcosa come myOnload e usarla come attributo my-onload. La funzione di compilazione verrà eseguita una volta che il modello è stato compilato (espressioni valutate e sotto-modelli caricati).

EDIT, 23 ore dopo: Ok, quindi ho fatto delle ricerche e ho anche posto la mia domanda . La domanda che ho posto era indirettamente correlata a questa domanda, ma casualmente mi ha portato alla risposta che risolve questa domanda.

La risposta è che puoi creare una semplice direttiva e inserire il tuo codice nella funzione di collegamento della direttiva, che (per la maggior parte dei casi d'uso, spiegata di seguito) verrà eseguita quando il tuo elemento è pronto / caricato. In base alla descrizione di Josh dell'ordine in cui vengono eseguite le funzioni di compilazione e collegamento ,

se hai questo markup:

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

Quindi AngularJS creerà le direttive eseguendo le funzioni di direttiva in un certo ordine:

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

Per impostazione predefinita, una funzione di "collegamento" diretta è un post-collegamento, quindi la funzione di collegamento della direttiva esterna1 non verrà eseguita fino a quando la funzione di collegamento della direttiva interna2 non sarà stata eseguita. Ecco perché diciamo che è sicuro solo manipolare DOM nel post-collegamento. Quindi, rispetto alla domanda originale, non dovrebbero esserci problemi di accesso all'html interno della direttiva figlia dalla funzione di collegamento della direttiva esterna, sebbene i contenuti inseriti dinamicamente debbano essere compilati, come detto sopra.

Da ciò possiamo concludere che possiamo semplicemente creare una direttiva per eseguire il nostro codice quando tutto è pronto / compilato / collegato / caricato:

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

Ora quello che puoi fare è inserire la direttiva ngElementReady sull'elemento radice dell'app e si console.logattiverà quando viene caricata:

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

È così semplice! Basta fare una semplice direttiva e usarla. ;)

Puoi personalizzarlo ulteriormente in modo che possa eseguire un'espressione (cioè una funzione) aggiungendovi $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.
            }
        };
    }]);

Quindi puoi usarlo su qualsiasi elemento:

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

Assicurati solo di avere le tue funzioni (ad esempio bodyIsReady e divIsReady) definite nell'ambito (nel controller) in cui risiede il tuo elemento.

Avvertenze: ho detto che funzionerà per la maggior parte dei casi. Fai attenzione quando usi determinate direttive come ngRepeat e ngIf. Creano il proprio ambito e la tua direttiva potrebbe non essere attivata. Ad esempio, se metti la nostra nuova direttiva ngElementReady su un elemento che ha anche ngIf e la condizione di ngIf restituisce false, la nostra direttiva ngElementReady non verrà caricata. Oppure, ad esempio, se metti la nostra nuova direttiva ngElementReady su un elemento che ha anche una direttiva ngInclude, la nostra direttiva non verrà caricata se il modello per ngInclude non esiste. Puoi aggirare alcuni di questi problemi assicurandoti di annidare le direttive invece di metterle tutte sullo stesso elemento. Ad esempio, in questo modo:

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

Invece di questo:

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

La direttiva ngElementReady verrà compilata nell'ultimo esempio, ma la sua funzione di collegamento non verrà eseguita. Nota: le direttive vengono sempre compilate, ma le loro funzioni di collegamento non vengono sempre eseguite a seconda di determinati scenari come sopra.

EDIT, pochi minuti dopo:

Oh, e per rispondere completamente alla domanda, puoi ora $emito il $broadcasttuo evento dall'espressione o dalla funzione che viene eseguita ng-element-readynell'attributo. :) Per esempio:

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

EDIT, ancora di più pochi minuti dopo:

Anche la risposta di @ satchmorun funziona, ma solo per il caricamento iniziale. Ecco una domanda SO molto utile che descrive l'ordine in cui le cose vengono eseguite, incluse le funzioni di collegamento app.rune altre. Quindi, a seconda del tuo caso d'uso, app.runpotrebbe essere buono, ma non per elementi specifici, nel qual caso le funzioni di collegamento sono migliori.

EDIT, cinque mesi dopo, 17 ottobre alle 8:11 PST:

Questo non funziona con i parziali caricati in modo asincrono. Dovrai aggiungere la contabilità ai tuoi parziali (ad esempio, un modo è far sì che ogni parziale tenga traccia di quando il suo contenuto è terminato il caricamento, quindi emette un evento in modo che l'ambito genitore possa contare quanti parziali sono stati caricati e infine fare ciò di cui ha bisogno dopo che tutti i parziali sono stati caricati).

EDIT, 23 ottobre alle 22:52 PST:

Ho creato una semplice direttiva per attivare del codice quando viene caricata un'immagine:

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

Ho migliorato la mia ngElementReadydirettiva originale e l'ho rinominata 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);
      }
    }
  }
}]);

Ad esempio, usalo in questo modo per attivare someFunctionquando un elemento viene caricato e {{placeholders}}non ancora sostituito:

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

someFunctionverrà chiamato prima che tutti i item.propertysegnaposto vengano sostituiti.

Valuta tutte le espressioni che desideri e crea l'ultima espressione trueda attendere per {{placeholders}}essere valutata in questo modo:

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

someFunctione anotherFunctionverrà licenziato dopo {{placeholders}}essere stato sostituito.

Funziona solo la prima volta che un elemento viene caricato, non su modifiche future. Potrebbe non funzionare come desiderato se un messaggio $digestcontinua a verificarsi dopo che i segnaposto sono stati inizialmente sostituiti (un $ digest può verificarsi fino a 10 volte fino a quando i dati non smettono di cambiare). Sarà adatto per la stragrande maggioranza dei casi d'uso.

EDIT, 31 ottobre alle 19:26 PST:

Va bene, questo è probabilmente il mio ultimo e ultimo aggiornamento. Questo probabilmente funzionerà per 99.999 dei casi d'uso là fuori:

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

Usalo in questo modo

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

Certo, probabilmente può essere ottimizzato, ma mi limiterò a farlo. requestAnimationFrame è carino.


3
Davvero fastidioso con tutti quei prefissi "data-". Sono contento di non utilizzarli personalmente.
stolsvik

1
@stolsvik eh, sì, nei browser più moderni non sono necessari.
trusktr

49
Merita un voto per la quantità di tempo e impegno dedicati a questa risposta. Bel lavoro!
GordyD

8
Bella risposta, ma valuta la possibilità di rimuovere tutte le righe "Modifica" e di ristrutturare un po 'la tua risposta. La cronologia delle modifiche è disponibile tramite il collegamento "modificato ..." in fondo alla risposta e distrae durante la lettura.
user247702

2
Il codice sorgente sarebbe davvero utile. E se puoi renderlo pubblico su npm, sarebbe PERFETTO. Risposta davvero bella, spiegata molto bene, +1 per la quantità di sforzo messo in questo.
tfrascaroli

38

Nella documentazione perangular.Module , c'è una voce che descrive la runfunzione:

Utilizzare questo metodo per registrare il lavoro da eseguire quando l'iniettore ha terminato di caricare tutti i moduli.

Quindi, se hai un modulo che è la tua app:

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

Puoi eseguire cose dopo che i moduli sono stati caricati con:

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

EDIT: inizializzazione manuale in soccorso

Quindi è stato sottolineato che il runnon viene chiamato quando il DOM è pronto e collegato. Viene chiamato quando il $injectorper il modulo a cui fa riferimento ng-appha caricato tutte le sue dipendenze, che è separato dalla fase di compilazione DOM.

Ho dato un'altra occhiata all'inizializzazione manuale e sembra che questo dovrebbe fare il trucco.

Ho creato un violino per illustrare .

L'HTML è semplice:

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

Notare la mancanza di un file ng-app. E ho una direttiva che eseguirà alcune manipolazioni DOM, così possiamo assicurarci dell'ordine e della tempistica delle cose.

Come al solito, viene creato un modulo:

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

Ed ecco la direttiva:

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

Sostituiremo il test-directivetag con un divof class test-directivee inseriremo il suo contenuto in un file h1.

Ho aggiunto una funzione di compilazione che restituisce sia le funzioni pre che post link in modo da poter vedere quando queste cose vengono eseguite.

Ecco il resto del codice:

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

Prima di fare qualsiasi cosa, non dovrebbero esserci elementi con una classe di test-directivenel DOM, e dopo aver finito dovrebbe esserci 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'));
});

È abbastanza semplice. Quando il documento è pronto, chiama angular.bootstrapcon l'elemento radice della tua app e un array di nomi di modulo.

In effetti, se colleghi una runfunzione al appmodulo , vedrai che viene eseguita prima che abbia luogo la compilazione.

Se esegui il violino e guardi la console, vedrai quanto segue:

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

2
grazie @satchmorun! ma run () viene eseguito prima della fine della parte di collegamento - appena verificato con alcuni console.logs.
Lior

ero curioso io stesso ... ho una direttiva che si attiva per implementare alcuni plugin jQuery DOM, runsi attiva prima della direttiva e quando viene eseguita, html non è tutto lì
charlietfl

@charlietfl - Ho approfondito un po 'il bootstrap manuale, ed è in realtà un modo abbastanza semplice per ottenere ciò che la domanda sta cercando. Ho aggiunto una modifica piuttosto lunga alla mia risposta originale.
satchmorun

5
ho scoperto che usando i $timeout( initMyPlugins,0)lavori all'interno della mia direttiva, tutto l'html di cui ho bisogno è lì
charlietfl,

@satchmorun, guarda questo follow-up: stackoverflow.com/questions/14989161/…
Lior

16

Angular non ha fornito un modo per segnalare quando una pagina ha terminato il caricamento, forse perché "finito" dipende dalla tua applicazione . Ad esempio, se hai un albero gerarchico dei parziali, uno carica gli altri. "Fine" significherebbe che sono stati caricati tutti. Qualsiasi framework farebbe fatica ad analizzare il tuo codice e a capire che tutto è fatto, o è ancora in attesa. Per questo, dovresti fornire una logica specifica per l'applicazione per verificarlo e determinarlo.


14

Ho trovato una soluzione che è relativamente accurata nel valutare quando l'inizializzazione angolare è completa.

La direttiva è:

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

Questo può quindi essere semplicemente aggiunto come attributo bodyall'elemento e quindi ascoltato per l'utilizzo$scope.$on('initialised', fn)

Funziona presupponendo che l'applicazione venga inizializzata quando non ci sono più cicli $ digest. $ watch viene chiamato ogni ciclo digest e quindi viene avviato un timer (setTimeout non $ timeout, quindi un nuovo ciclo digest non viene attivato). Se un ciclo di digest non si verifica entro il timeout, si presume che l'applicazione sia stata inizializzata.

Ovviamente non è preciso come la soluzione satchmoruns (poiché è possibile che un ciclo di digest richieda più tempo del timeout) ma la mia soluzione non richiede che tu tenga traccia dei moduli, il che lo rende molto più facile da gestire (in particolare per progetti più grandi ). Ad ogni modo, sembra essere abbastanza preciso per le mie esigenze. Spero che sia d'aiuto.


Ottima soluzione. Per progetti in cui tutto il codice in uno o due file compressi funziona molto bene.
merqlove

1
Questa è una soluzione fantastica Nel caso in cui tu abbia molto codice in jquery e stai cercando di convertire il codice in angolare passo dopo passo, questo ha perfettamente senso.
Mangesh Pimpalkar

11

Se stai usando Angular UI Router , puoi ascoltare l' $viewContentLoadedevento.

"$ viewContentLoaded - attivato una volta caricata la visualizzazione, dopo il rendering del DOM ." $ scope "della visualizzazione genera l'evento." - Link

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

3
$ scope. $ watch ('$ viewContentLoaded', function () ha fatto il trucco per me
Louis XIV,

2
downvoted per "ciò che dovresti essere". E se dicessi "se stai usando React invece di Angular (quale dovresti essere) ..."? Non è proprio un atteggiamento da avere in questo ecosistema IMHO.
Valentin Waeselynck,

@ValentinWaeselynck hai assolutamente ragione. Ho modificato la mia risposta per rimuovere il mio pregiudizio.
Jordan Skole

1
Ha funzionato per me! Grazie. L'ho effettivamente aggiunto alla mia funzione di esecuzione, quindi ho cambiato $ scope in $ rootScope.
JDavies

2
Come ha sottolineato Angular University in una risposta diversa, $ viewContentLoaded potrebbe non essere presente originariamente, ma ora funziona nel provider ngRoute integrato, esattamente allo stesso modo. Con questo in mente, penso che questa sia la risposta rapida, semplice e leggibile che molti (la maggior parte?) Futuri lettori cercheranno.
Kevin Crumley

3

osservo la manipolazione DOM di angular con JQuery e ho impostato una finitura per la mia app (una sorta di situazione predefinita e soddisfacente di cui ho bisogno per il mio abstract app), ad esempio mi aspetto che il mio ng-repeater produca 7 risultati e lì per i imposterà una funzione di osservazione con l'aiuto di setInterval per questo scopo.

$(document).ready(function(){

  var interval = setInterval(function(){

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

  },50);

});

3
Non lo farei. Usare gli intervalli per verificare che le cose accadano non è una buona pratica, non è scalabile e ci sono altri modi per far accadere le cose. I timer servono per svolgere attività concrete che devono essere eseguite dopo un certo periodo di tempo, non per "indovinare" quando i contenuti oi risultati sono pronti.
dudewad

Per non parlare del fatto che l'uso di un timer jquery contro la piattaforma angular è controproducente: angular ha una classe di timeout e dovresti usarla altrimenti sei a cavallo di due framework e diventa confuso molto rapidamente.
dudewad

3

Se non usi il modulo ngRoute , cioè non hai l' evento $ viewContentLoaded .

Puoi usare un altro metodo di direttiva:

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

Di conseguenza, alla risposta di trusktr ha la priorità più bassa. Inoltre $ timeout farà sì che Angular esegua un intero ciclo di eventi prima dell'esecuzione del callback.

$ rootScope utilizzato, perché consente di posizionare la direttiva in qualsiasi ambito dell'applicazione e notificare solo i listener necessari.

$ rootScope. $ emit attiverà un evento per tutti i $ rootScope. $ solo sui listener. La parte interessante è che $ rootScope. $ Broadcast notificherà a tutti $ rootScope. $ On così come $ scope. $ On listener Fonte


2

Secondo il team di Angular e questo problema di Github :

ora abbiamo gli eventi $ viewContentLoaded e $ includeContentLoaded che vengono emessi rispettivamente in ng-view e ng-include. Penso che questo sia il più vicino possibile a sapere quando abbiamo finito con la compilation.

Sulla base di ciò, sembra che al momento non sia possibile farlo in modo affidabile, altrimenti Angular avrebbe fornito l'evento fuori dagli schemi.

Il bootstrap dell'app implica l'esecuzione del ciclo digest sull'ambito radice e inoltre non è presente un evento di ciclo digest completato.

Secondo i documenti di progettazione di Angular 2 :

A causa di più digest, è impossibile determinare e notificare al componente che il modello è stabile. Questo perché la notifica può modificare ulteriormente i dati, il che può riavviare il processo di associazione.

Secondo questo, il fatto che ciò non sia possibile è uno dei motivi per cui è stata presa la decisione di riscrivere in Angular 2.


2

Avevo un frammento che veniva caricato dopo / dal parziale principale arrivato tramite il routing.

Avevo bisogno di eseguire una funzione dopo che quel sottoparziale è stato caricato e non volevo scrivere una nuova direttiva e ho capito che potresti usare un ngIf

Titolare del genitore parziale:

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

HTML di sottoparziale

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

1

Se desideri generare JS con dati lato server (JSP, PHP) puoi aggiungere la tua logica a un servizio, che verrà caricato automaticamente quando il controller viene caricato.

Inoltre, se si desidera reagire quando tutte le direttive sono state compilate / collegate, è possibile aggiungere le soluzioni proposte appropriate sopra nella logica di inizializzazione.

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

    // add your initialization logic here

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


module.controller('YourController', function (YourControllerInitService) {
});

0

Queste sono tutte ottime soluzioni, tuttavia, se stai attualmente utilizzando Routing, ho trovato questa soluzione la più semplice e la minima quantità di codice necessaria. Utilizzo della proprietà "risoluzione" per attendere il completamento di una promessa prima di attivare il percorso. per esempio

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

})

Fare clic qui per la documentazione completa - Ringraziamo K. Scott Allen


0

forse posso aiutarti con questo esempio

Nella fancybox personalizzata mostro i contenuti con valori interpolati.

nel servizio, nel metodo fancybox "aperto", lo faccio

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

$ compile restituisce dati compilati. puoi controllare i dati compilati

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.