Envío de evento cuando AngularJS terminó de cargarse

114

Se preguntó cuál es la mejor manera de detectar el final de la carga / arranque de la página, cuando todas las directivas terminan de compilar / vincular.

¿Algún evento ya está ahí? ¿Debo sobrecargar la función de arranque?

Lior
fuente

Respuestas:

204

Solo una corazonada: ¿por qué no mirar cómo lo hace la directiva ngCloak? Claramente, la directiva ngCloak logra mostrar contenido después de que se hayan cargado las cosas. Apuesto a que mirar ngCloak conducirá a la respuesta exacta ...

EDITAR 1 hora después: Ok, bueno, miré ngCloak y es realmente corto. Lo que esto obviamente implica es que la función de compilación no se ejecutará hasta que se hayan evaluado las expresiones {{plantilla}} (es decir, la plantilla que cargó), por lo tanto, la buena funcionalidad de la directiva ngCloak.

Mi conjetura educada sería simplemente hacer una directiva con la misma simplicidad de ngCloak, luego, en su función de compilación, haga lo que quiera hacer. :) Coloque la directiva en el elemento raíz de su aplicación. Puede llamar a la directiva algo como myOnload y usarla como atributo my-onload. La función de compilación se ejecutará una vez que la plantilla haya sido compilada (expresiones evaluadas y sub-plantillas cargadas).

EDITAR, 23 horas después: Ok, investigué un poco y también hice mi propia pregunta . La pregunta que hice estaba indirectamente relacionada con esta pregunta, pero casualmente me llevó a la respuesta que resuelve esta pregunta.

La respuesta es que puede crear una directiva simple y poner su código en la función de enlace de la directiva, que (para la mayoría de los casos de uso, se explica a continuación) se ejecutará cuando su elemento esté listo / cargado. Basado en la descripción de Josh del orden en el que se ejecutan las funciones de compilación y enlace ,

si tiene este marcado:

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

Entonces AngularJS creará las directivas ejecutando funciones directivas en un orden determinado:

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

Por defecto, una función de "enlace" directo es un post-enlace, por lo que la función de enlace de la directiva externa1 no se ejecutará hasta que se haya ejecutado la función de enlace de la directiva interna2. Es por eso que decimos que solo es seguro realizar la manipulación DOM en el enlace posterior. Entonces, hacia la pregunta original, no debería haber ningún problema para acceder al html interno de la directiva secundaria desde la función de enlace de la directiva externa, aunque los contenidos insertados dinámicamente deben compilarse, como se dijo anteriormente.

De esto podemos concluir que simplemente podemos hacer una directiva para ejecutar nuestro código cuando todo esté listo / compilado / vinculado / cargado:

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

Ahora, lo que puede hacer es colocar la directiva ngElementReady en el elemento raíz de la aplicación y se console.logactivará cuando se cargue:

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

¡Es así de simple! Simplemente haga una directiva simple y úsela. ;)

Puede personalizarlo aún más para que pueda ejecutar una expresión (es decir, una función) agregándole $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.
            }
        };
    }]);

Entonces puedes usarlo en cualquier elemento:

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

Solo asegúrese de tener sus funciones (por ejemplo, bodyIsReady y divIsReady) definidas en el alcance (en el controlador) bajo el cual vive su elemento.

Advertencias: dije que esto funcionaría para la mayoría de los casos. Tenga cuidado al usar ciertas directivas como ngRepeat y ngIf. Crean su propio alcance y es posible que su directiva no se active. Por ejemplo, si coloca nuestra nueva directiva ngElementReady en un elemento que también tiene ngIf, y la condición de ngIf se evalúa como falsa, entonces nuestra directiva ngElementReady no se cargará. O, por ejemplo, si coloca nuestra nueva directiva ngElementReady en un elemento que también tiene una directiva ngInclude, nuestra directiva no se cargará si la plantilla para ngInclude no existe. Puede solucionar algunos de estos problemas asegurándose de anidar las directivas en lugar de ponerlas todas en el mismo elemento. Por ejemplo, haciendo esto:

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

en lugar de esto:

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

La directiva ngElementReady se compilará en el último ejemplo, pero su función de enlace no se ejecutará. Nota: las directivas siempre se compilan, pero sus funciones de enlace no siempre se ejecutan dependiendo de ciertos escenarios como el anterior.

EDITAR, unos minutos después:

Ah, y para responder completamente a la pregunta, puede ahora $emito $broadcastsu evento desde la expresión o función que se ejecuta en el ng-element-readyatributo. :) P.ej:

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

EDITAR, incluso más minutos después:

La respuesta de @ satchmorun también funciona, pero solo para la carga inicial. Aquí hay una pregunta SO muy útil que describe el orden en que se ejecutan las cosas, incluidas las funciones de enlace app.run, y otras. Entonces, dependiendo de su caso de uso, app.runpodría ser bueno, pero no para elementos específicos, en cuyo caso las funciones de enlace son mejores.

EDITAR, cinco meses después, 17 de octubre a las 8:11 PST:

Esto no funciona con parciales que se cargan de forma asincrónica. Deberá agregar contabilidad a sus parciales (por ejemplo, una forma es hacer que cada parcial lleve un registro de cuándo se carga su contenido y luego emitir un evento para que el alcance principal pueda contar cuántos parciales se han cargado y finalmente hacer lo que necesita hacer después de cargar todos los parciales).

EDITAR, 23 de octubre a las 10:52 pm PST:

Hice una directiva simple para disparar un código cuando se carga una imagen:

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

EDITAR, 24 de octubre a las 12:48 am PST:

Mejoré mi ngElementReadydirectiva original y le cambié el nombre a 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);
      }
    }
  }
}]);

Por ejemplo, utilícelo así para disparar someFunctioncuando un elemento está cargado y {{placeholders}}aún no se ha reemplazado:

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

someFunctionse llamará antes de que item.propertyse reemplacen todos los marcadores de posición.

Evalúe tantas expresiones como desee y haga que la última expresión truea esperar {{placeholders}}sea ​​evaluada de esta manera:

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

someFunctiony anotherFunctionserá despedido después de {{placeholders}}haber sido reemplazado.

Esto solo funciona la primera vez que se carga un elemento, no en cambios futuros. Es posible que no funcione como se desea si $digestsigue sucediendo después de que los marcadores de posición hayan sido reemplazados inicialmente (un $ digest puede ocurrir hasta 10 veces hasta que los datos dejen de cambiar). Será adecuado para la gran mayoría de casos de uso.

EDITAR, 31 de octubre a las 7:26 pm PST:

Muy bien, esta es probablemente mi última y última actualización. Esto probablemente funcionará para 99,999 de los casos de uso que existen:

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

Úselo así

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

Por supuesto, probablemente se pueda optimizar, pero lo dejaré así. requestAnimationFrame es agradable.

trusktr
fuente
3
Realmente molesto con todos esos prefijos de "datos". Me alegro de no usarlos yo mismo.
stolsvik
1
@stolsvik je, sí, en los navegadores más modernos no son necesarios.
trusktr
49
Merece una votación por la cantidad de tiempo y esfuerzo invertidos en esta respuesta. ¡Buen trabajo!
GordyD
8
Buena respuesta, pero considere eliminar todas las líneas "Editar" y reestructurar un poco su respuesta. El historial de edición está disponible a través del enlace "editado ..." en la parte inferior de su respuesta, y distrae mientras lee.
user247702
2
El código fuente sería realmente útil. Y si puede hacerlo público en npm, sería PERFECTO. Muy buena respuesta, muy bien explicada, +1 por la cantidad de esfuerzo invertido en esto.
tfrascaroli
38

En los documentos deangular.Module , hay una entrada que describe la runfunción:

Utilice este método para registrar el trabajo que debe realizarse cuando el inyector haya terminado de cargar todos los módulos.

Entonces, si tiene algún módulo que sea su aplicación:

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

Puede ejecutar cosas después de que los módulos se hayan cargado con:

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

EDITAR: Inicialización manual al rescate.

Por lo tanto, se ha señalado que runno se llama cuando el DOM está listo y vinculado. Se llama cuando el $injectormódulo al que se hace referencia ng-appha cargado todas sus dependencias, lo que es independiente del paso de compilación DOM.

Eché otro vistazo a la inicialización manual , y parece que esto debería funcionar.

Hice un violín para ilustrar .

El HTML es simple:

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

Tenga en cuenta la falta de un ng-app. Y tengo una directiva que hará algo de manipulación DOM, para que podamos asegurarnos del orden y el tiempo de las cosas.

Como es habitual, se crea un módulo:

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

Y aquí está la directiva:

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

Reemplazaremos la test-directiveetiqueta con una divde clase test-directivey envolveremos su contenido en una h1.

Agregué una función de compilación que devuelve funciones de enlace anteriores y posteriores para que podamos ver cuándo se ejecutan estas cosas.

Aquí está el resto del código:

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

Antes de que hayamos hecho algo, no debería haber elementos con una clase de test-directiveen el DOM, y una vez que hayamos terminado, debería haber 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'));
});

Es bastante sencillo. Cuando el documento esté listo, llame angular.bootstrapcon el elemento raíz de su aplicación y una matriz de nombres de módulo.

De hecho, si adjunta una runfunción al appmódulo , verá que se ejecuta antes de que se realice la compilación.

Si ejecuta el violín y mira la consola, verá lo siguiente:

before (should be 0): 0 
Compiling test-directive 
Prelink
Postlink
after (should be 1): 1 <--- success!
satchmorun
fuente
2
gracias @satchmorun! pero run () se ejecuta antes del final de la parte de vinculación, simplemente verifíquelo con algunos console.logs.
Lior
Yo mismo tenía curiosidad ... Tengo una directiva que se activa para implementar algunos complementos de DOM de jQuery, se runactiva antes de la directiva y cuando se ejecuta, html no está todo allí
charlietfl
@charlietfl: profundicé un poco en el bootstrapping manual, y en realidad es una manera bastante fácil de obtener lo que busca la pregunta. Agregué una edición bastante larga a mi respuesta original.
satchmorun
5
$timeout( initMyPlugins,0)
Descubrí
@satchmorun, vea este seguimiento: stackoverflow.com/questions/14989161/…
Lior
16

Angular no ha proporcionado una forma de indicar cuando una página terminó de cargarse, tal vez porque "terminado" depende de su aplicación . Por ejemplo, si tiene un árbol jerárquico de parciales, uno carga a los demás. "Finalizar" significaría que se han cargado todos. Cualquier marco tendría dificultades para analizar su código y comprender que todo está hecho o que aún se espera. Para eso, tendría que proporcionar una lógica específica de la aplicación para verificarlo y determinarlo.

Lior
fuente
14

Se me ocurrió una solución que es relativamente precisa para evaluar cuándo se completa la inicialización angular.

La directiva es:

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

Eso puede agregarse como un atributo al bodyelemento y luego escucharse para usar$scope.$on('initialised', fn)

Funciona asumiendo que la aplicación se inicializa cuando no hay más ciclos de $ digest. $ watch se llama en cada ciclo de resumen y, por lo tanto, se inicia un temporizador (setTimeout no $ timeout, por lo que no se activa un nuevo ciclo de resumen). Si no se produce un ciclo de resumen dentro del tiempo de espera, se supone que la aplicación se ha inicializado.

Obviamente, no es tan precisa como la solución satchmoruns (ya que es posible que un ciclo de resumen demore más que el tiempo de espera), pero mi solución no necesita que realice un seguimiento de los módulos, lo que hace que sea mucho más fácil de administrar (especialmente para proyectos más grandes ). De todos modos, parece ser lo suficientemente preciso para mis requisitos. Espero eso ayude.

Theo
fuente
Excelente solucion. Para proyectos en los que todo el código de uno o dos archivos comprimidos funciona muy bien.
merqlove
1
Esta es una solución fantástica En caso de que tenga mucho código en jquery y esté tratando de convertir el código a angular paso a paso, esto tiene mucho sentido.
Mangesh Pimpalkar
11

Si está utilizando Angular UI Router , puede escuchar el $viewContentLoadedevento.

"$ viewContentLoaded: se activa una vez que se carga la vista, después de que se procesa el DOM . El '$ scope' de la vista emite el evento". - Enlace

$scope.$on('$viewContentLoaded', 
function(event){ ... });
Jordan Skole
fuente
3
$ scope. $ watch ('$ viewContentLoaded', function () hizo el truco para mí
Luis XIV
2
votó en contra de 'lo que debería ser'. ¿Qué pasa si digo "si estás usando React en lugar de Angular (que deberías ser) ..."? No es una actitud muy para tener en este ecosistema en mi humilde opinión.
Valentin Waeselynck
@ValentinWaeselynck, tienes toda la razón. Edité mi respuesta para eliminar mi sesgo.
Jordan Skole
1
¡Trabajó para mi! Gracias. De hecho, lo agregué a mi función de ejecución, luego cambié $ scope a $ rootScope.
JDavies
2
Como señaló la Universidad Angular en una respuesta diferente, es posible que $ viewContentLoaded no haya estado allí originalmente, pero ahora funciona en el proveedor ngRoute integrado, exactamente de la misma manera. Con eso en mente, creo que esta es la respuesta rápida, simple y legible que muchos (¿la mayoría?) Futuros lectores estarán buscando.
Kevin Crumley
3

Observo la manipulación DOM de angular con JQuery y establecí un final para mi aplicación (algún tipo de situación predefinida y satisfactoria que necesito para mi resumen de la aplicación) por ejemplo, espero que mi ng-repetidor produzca 7 resultados y ahí para mí establecerá una función de observación con la ayuda de setInterval para este propósito.

$(document).ready(function(){

  var interval = setInterval(function(){

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

  },50);

});
Hassan Gilak
fuente
3
Yo no haría esto. Usar intervalos para comprobar si suceden cosas no es una buena práctica, no se puede escalar y hay otras formas de hacer que las cosas sucedan. Los temporizadores sirven para realizar tareas concretas que deben realizarse después de un período de tiempo determinado, no para "adivinar" cuándo el contenido o los resultados están listos.
dudewad
Sin mencionar que usar un temporizador jquery contra la plataforma angular es contraproducente: angular tiene una clase de tiempo de espera y debe usarla, de lo contrario, está a caballo entre dos marcos y se vuelve confuso muy rápidamente.
dudewad
3

Si no usa el módulo ngRoute , es decir, no tiene el evento $ viewContentLoaded .

Puede utilizar otro método de directiva:

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

De acuerdo con la respuesta de trusktr, tiene la prioridad más baja. Además, $ timeout hará que Angular se ejecute a través de un ciclo de eventos completo antes de la ejecución de la devolución de llamada.

Se utilizó $ rootScope , porque permite colocar la directiva en cualquier ámbito de la aplicación y notificar solo a los oyentes necesarios.

$ rootScope. $ emit disparará un evento para todos los $ rootScope. $ solo en oyentes. La parte interesante es que $ rootScope. $ Broadcast notificará a todos los $ rootScope. $ On y $ scope. $ On oyentes Fuente

Vadim P.
fuente
2

Según el equipo de Angular y este problema de Github :

ahora tenemos los eventos $ viewContentLoaded y $ includeContentLoaded que se emiten en ng-view y ng-include respectivamente. Creo que esto es lo más cercano a saber cuando hemos terminado con la compilación.

En base a esto, parece que actualmente no es posible hacerlo de una manera confiable, de lo contrario Angular habría proporcionado el evento listo para usar.

Arrancar la aplicación implica ejecutar el ciclo de resumen en el alcance raíz, y tampoco hay un evento de finalización del ciclo de resumen.

Según los documentos de diseño de Angular 2 :

Debido a múltiples resúmenes, es imposible determinar y notificar al componente que el modelo es estable. Esto se debe a que la notificación puede cambiar aún más los datos, lo que puede reiniciar el proceso de vinculación.

De acuerdo con esto, el hecho de que esto no sea posible es una de las razones por las que se tomó la decisión de optar por una reescritura en Angular 2.

Universidad Angular
fuente
2

Tenía un fragmento que se estaba cargando después / por el parcial principal que ingresó a través del enrutamiento.

Necesitaba ejecutar una función después de que se cargó ese subparcial y no quería escribir una nueva directiva y descubrí que podría usar un descarado ngIf

Controlador de padre parcial:

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

HTML de subparcial

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

Si desea generar JS con datos del lado del servidor (JSP, PHP), puede agregar su lógica a un servicio, que se cargará automáticamente cuando se cargue su controlador.

Además, si desea reaccionar cuando todas las directivas hayan terminado de compilar / vincular, puede agregar las soluciones propuestas apropiadas arriba en la lógica de inicialización.

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

Todas estas son excelentes soluciones. Sin embargo, si actualmente está utilizando enrutamiento, encontré que esta solución es la más fácil y la menor cantidad de código necesaria. Usar la propiedad 'resolver' para esperar a que se complete una promesa antes de activar la ruta. p.ej

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

})

Haga clic aquí para ver los documentos completos - Crédito a K. Scott Allen

aholtry
fuente
0

puede ser que pueda ayudarte con este ejemplo

En el fancybox personalizado muestro contenidos con valores interpolados.

en el servicio, en el método fancybox "abierto", lo hago

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

$ compile devuelve datos compilados. puedes comprobar los datos compilados

Shailendra Pathak
fuente