versiones varios español ejemplos directivas controladores javascript jquery dom angularjs

javascript - varios - AngularJS: ¿Cómo ejecutar código adicional después de que AngularJS haya generado una plantilla?



varios controladores angularjs (12)

Creo que está buscando $ evalAsync http://docs.angularjs.org/api/ng.$rootScope.Scope#$evalAsync

Tengo una plantilla angular en el DOM. Cuando mi controlador obtiene datos nuevos de un servicio, actualiza el modelo en $ scope y vuelve a presentar la plantilla. Todo bien hasta ahora.

El problema es que también necesito hacer un trabajo extra después de que la plantilla se haya re-renderizado y esté en el DOM (en este caso un plugin de jQuery).

Parece que debería haber un evento para escuchar, como AfterRender, pero no puedo encontrar tal cosa. Tal vez una directiva sería un camino por recorrer, pero parecía dispararse demasiado temprano también.

Aquí hay un jsFiddle que resume mi problema: Fiddle-AngularIssue

== ACTUALIZACIÓN ==

Basándome en comentarios útiles, he cambiado a una directiva para manejar la manipulación DOM, e implementado un modelo $ watch dentro de la directiva. Sin embargo, todavía estoy teniendo el mismo problema de base; el código dentro del evento $ watch se activa antes de que la plantilla se haya compilado e insertado en el DOM, por lo tanto, el complemento jquery siempre está evaluando una tabla vacía.

Curiosamente, si elimino la llamada asincrónica todo funciona bien, por lo que es un paso en la dirección correcta.

Aquí está mi Fiddle actualizado para reflejar estos cambios: http://jsfiddle.net/uNREn/12/


En algunos escenarios donde actualiza un servicio y lo redirecciona a una nueva vista (página) y luego su directiva se carga antes de que se actualicen sus servicios, puede usar $ rootScope. $ Broadcast si su $ watch o $ timeout falla

Ver

<service-history log="log" data-ng-repeat="log in requiedData"></service-history>

Controlador

app.controller("MyController",[''$scope'',''$rootScope'', function($scope, $rootScope) { $scope.$on(''$viewContentLoaded'', function () { SomeSerive.getHistory().then(function(data) { $scope.requiedData = data; $rootScope.$broadcast("history-updation"); }); }); }]);

Directiva

app.directive("serviceHistory", function() { return { restrict: ''E'', replace: true, scope: { log: ''='' }, link: function($scope, element, attrs) { function updateHistory() { if(log) { //do something } } $rootScope.$on("history-updation", updateHistory); } }; });


Esta publicación es antigua, pero cambio tu código a:

scope.$watch("assignments", function (value) {//I change here var val = value || null; if (val) element.dataTable({"bDestroy": true}); }); }

ver: http://jsfiddle.net/dnzY9/

Espero que te ayude


Finalmente encontré la solución, estaba usando un servicio REST para actualizar mi colección. Para convertir datatable jquery es el siguiente código:

$scope.$watchCollection( ''conferences'', function( old, nuew ) { if( old === nuew ) return; $( ''#dataTablex'' ).dataTable().fnDestroy(); $timeout(function () { $( ''#dataTablex'' ).dataTable(); }); });


He encontrado que la solución más simple (barata y alegre) es simplemente agregar un tramo vacío con ng-show = "someFunctionThatAlwaysReturnsZeroOrNothing ()" al final del último elemento procesado. Esta función se ejecutará cuando se compruebe si se debe mostrar el elemento span. Ejecuta cualquier otro código en esta función.

Me doy cuenta de que esta no es la forma más elegante de hacer las cosas, sin embargo, funciona para mí ...

Tuve una situación similar, aunque ligeramente invertida, donde necesitaba quitar un indicador de carga cuando comenzaba una animación, en dispositivos móviles el angular se inicializaba mucho más rápido que la animación que se mostraría, y usar un ng-cloak era insuficiente ya que el indicador de carga era eliminado bien antes de que se muestren datos reales. En este caso, agregué la función my return 0 al primer elemento renderizado, y en esa función volteé la var que oculta el indicador de carga. (Por supuesto, agregué un ng-hide al indicador de carga activado por esta función.


He tenido que hacer esto bastante a menudo. Tengo una directiva y necesito hacer algunas cosas de jquery después de que las cosas del modelo estén completamente cargadas en el DOM. entonces pongo mi lógica en el enlace: función de la directiva y envuelvo el código en un setTimeout (función () {.....}, 1); setTimout se activará después de que se haya cargado el DOM y 1 milisegundo es la cantidad de tiempo más corta después de que DOM se cargue antes de que se ejecute el código. esto parece funcionar para mí, pero desearía que angular surgiera un evento una vez que se haya terminado de cargar la plantilla, de modo que las directivas utilizadas por esa plantilla puedan hacer cosas jquery y acceder a elementos DOM. espero que esto ayude.


Ni $ scope. $ EvalAsync () ni $ timeout (fn, 0) funcionaron de manera confiable para mí.

Tuve que combinar los dos. Hice una directiva y también puse una prioridad más alta que el valor predeterminado para una buena medida. Aquí hay una directiva para él (Nota: uso ngInject para inyectar dependencias):

app.directive(''postrenderAction'', postrenderAction); /* @ngInject */ function postrenderAction($timeout) { // ### Directive Interface // Defines base properties for the directive. var directive = { restrict: ''A'', priority: 101, link: link }; return directive; // ### Link Function // Provides functionality for the directive during the DOM building/data binding stage. function link(scope, element, attrs) { $timeout(function() { scope.$evalAsync(attrs.postrenderAction); }, 0); } }

Para llamar a la directiva, harías esto:

<div postrender-action="functionToRun()"></div>

Si desea llamarlo después de que se haya ejecutado una repetición ng, agregué un span vacío en mi ng-repeat y ng-if = "$ last":

<li ng-repeat="item in list"> <!-- Do stuff with list --> ... <!-- Fire function after the last element is rendered --> <span ng-if="$last" postrender-action="$ctrl.postRender()"></span> </li>


Primero, el lugar correcto para meterse con el renderizado son las directivas. Mi consejo sería que DOM manipule los plugins de jQuery con directivas como esta.

Tuve el mismo problema y se me ocurrió este fragmento. Utiliza $watch y $evalAsync para garantizar que su código se ejecute después de que se hayan resuelto las directivas como ng-repeat hayan generado plantillas como {{ value }} .

app.directive(''name'', function() { return { link: function($scope, element, attrs) { // Trigger when number of children changes, // including by directives like ng-repeat var watch = $scope.$watch(function() { return element.children().length; }, function() { // Wait for templates to render $scope.$evalAsync(function() { // Finally, directives are evaluated // and templates are renderer here var children = element.children(); console.log(children); }); }); }, }; });

Espero que esto te ayude a evitar algunas dificultades.


Puede usar el módulo ''jQuery Passthrough'' de angular-ui utils . Conecté con éxito un plugin de jQuery touch carrusel a algunas imágenes que recupero de un servicio web y las renderizo con ng-repeat.


Siguiendo el consejo de Misko, si quieres una operación asincrónica, en lugar de $ timeout () (que no funciona)

$timeout(function () { $scope.assignmentsLoaded(data); }, 1000);

use $ evalAsync () (que funciona)

$scope.$evalAsync(function() { $scope.assignmentsLoaded(data); } );

Fiddle . También agregué un enlace "eliminar fila de datos" que modificará $ scope.assignments, simulando un cambio en los datos / modelo, para mostrar que el cambio de datos funciona.

La sección Runtime de la página Conceptual Overview Overview explica que evalAsync debe usarse cuando necesite que ocurra algo fuera del marco de pila actual, pero antes de que el navegador lo renderice. (Adivinar aquí ... "marco de pila actual" probablemente incluya actualizaciones de DOM angular). Use $ timeout si necesita que ocurra algo después de que el navegador lo renderice.

Sin embargo, como ya descubrió, no creo que haya ninguna necesidad de una operación asincrónica aquí.


También puede crear una directiva que ejecute su código en la función de enlace.

Mira esa respuesta .


Vine con una solución bastante simple. No estoy seguro de si es la forma correcta de hacerlo, pero funciona en un sentido práctico. Veamos directamente lo que queremos que se represente. Por ejemplo, en una directiva que incluye algunos ng-repeat s, me gustaría ver la longitud del texto (¡puede que tenga otras cosas!) De párrafos o todo el html. La directiva será así:

.directive(''myDirective'', [function () { ''use strict''; return { link: function (scope, element, attrs) { scope.$watch(function(){ var whole_p_length = 0; var ps = element.find(''p''); for (var i=0;i<ps.length;i++){ if (ps[i].innerHTML == undefined){ continue } whole_p_length+= ps[i].innerHTML.length; } //it could be this too: whole_p_length = element[0].innerHTML.length; but my test showed that the above method is a bit faster console.log(whole_p_length); return whole_p_length; }, function (value) { //Code you want to be run after rendering changes }); } }]);

TEN EN CUENTA que el código realmente se ejecuta después de renderizar los renderizados en lugar de renderizado completo. Pero supongo que en la mayoría de los casos puedes manejar las situaciones cada vez que ocurren cambios de representación. También podría pensar en comparar esta longitud de p s (o cualquier otra medida) con su modelo si desea ejecutar su código una sola vez después de completar la representación. Agradezco cualquier pensamiento / comentario sobre esto.