Juste une idée: pourquoi ne pas regarder la façon dont les ngCloak la directive est-il? Clairement le ngCloak directive parvient à afficher le contenu après que les choses ont chargé. Je parie que en regardant ngCloak conduira à la réponse exacte...
EDIT 1 heure plus tard:
Ok, eh bien, j'ai regardé ngCloak et c'est vraiment court. Ce que cela implique, bien entendu, est que la compilation de la fonction ne sera pas exécutée jusqu'à ce que {{modèle}} les expressions ont été évalués (c'est à dire le modèle, il chargé), ainsi que la fonctionnalité de nice le ngCloak directive.
Mon hypothèse serait de simplement faire une directive avec la même simplicité d'ngCloak, puis dans votre compiler la fonction de faire ce que vous voulez faire. :) Place de la directive sur l'élément racine de votre application. Vous pouvez appeler la directive quelque chose comme myOnload et l'utiliser comme un attribut de ma-onload. La compilation en fonction ne s'exécute une fois que le modèle a été compilé (expressions évaluées et sous-modèles chargé).
EDIT, 23 heures plus tard:
Ok, j'ai donc fait quelques recherches, et j'ai également demandé à ma propre question. La question que j'ai posée était indirectement liés à cette question, mais il coïncidence me conduire à la réponse qui résout cette question.
La réponse est que vous pouvez créer une simple directive et de mettre votre code dans la directive de la fonction de lien, ce qui (pour la plupart des cas, expliqué ci-dessous) sera exécuté lorsque votre élément est prêt/chargé. Basé sur Josh, la description de l'ordre dans lequel les compiler et lier les fonctions sont exécutées,
si vous avez cette balise:
<div directive1>
<div directive2>
<!-- ... -->
</div>
</div>
Ensuite, AngularJS va créer les directives en cours d'exécution de la directive
fonctions dans un certain ordre:
directive1: compile
directive2: compile
directive1: controller
directive1: pre-link
directive2: controller
directive2: pre-link
directive2: post-link
directive1: post-link
Par défaut droite "lien" de la fonction est un post-lien, de sorte que votre extérieur
directive1 relative de la fonction de lien ne fonctionnera pas jusqu'à ce que après l'intérieure
directive2 de la fonction de lien a couru. C'est pourquoi nous disons que c'est seulement
pas de danger de manipulation du DOM dans le post-link. Vers l'original
question, il devrait y avoir aucun problème d'accès à l'enfant de la directive
intérieure html à partir de l'extérieur de la directive en fonction de lien, si
insérés dynamiquement le contenu doit être compilé, comme dit ci-dessus.
De cela, nous pouvons conclure que l'on peut se contenter de faire une directive pour exécuter notre code quand tout est prêt/compilé/lien/chargé:
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.
}
};
}]);
Maintenant, ce que vous pouvez faire est de mettre la ngElementReady directive sur l'élément racine de l'application, et l' console.log
le feu quand il est chargé:
<body data-ng-app="MyApp" data-ng-element-ready="">
...
...
</body>
C'est très simple! Il suffit d'une simple directive et de l'utiliser. ;)
Vous pouvez le personnaliser de sorte qu'il peut exécuter une expression (c'est à dire une fonction) par l'ajout d' $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.
}
};
}]);
Ensuite, vous pouvez l'utiliser sur n'importe quel élément:
<body data-ng-app="MyApp" data-ng-controller="BodyCtrl" data-ng-element-ready="bodyIsReady()">
...
<div data-ng-element-ready="divIsReady()">...<div>
</body>
Assurez-vous d'avoir vos fonctions (par exemple, bodyIsReady et divIsReady) défini dans le champ d'application (le contrôleur) que votre élément de vie.
Mises en garde: j'ai dit que cela fonctionnera pour la plupart des cas. Être prudent lors de l'utilisation de certaines directives, comme ngRepeat et ngIf. Ils créent leur propre champ d'application, et votre directive ne peut pas le feu. Par exemple, si vous avez mis notre nouveau ngElementReady directive sur un élément qui a également ngIf, et la condition de la ngIf évalue à false, alors notre ngElementReady la directive ne sera pas chargée. Ou, par exemple, si vous avez mis notre nouveau ngElementReady directive sur un élément qui a aussi un ngInclude directive, la directive ne sera pas chargé si le modèle du ngInclude n'existe pas. Vous pouvez obtenir autour de certains de ces problèmes en vous assurant de nest les directives au lieu de les mettre tous sur le même élément. Par exemple, en faisant ceci:
<div data-ng-element-ready="divIsReady()">
<div data-ng-include="non-existent-template.html"></div>
<div>
au lieu de cela:
<div data-ng-element-ready="divIsReady()" data-ng-include="non-existent-template.html"></div>
Le ngElementReady directive seront compilés dans le dernier exemple, mais c'est fonction de lien ne sera pas exécuté. Remarque: les directives sont toujours compilé, mais le lien avec leurs fonctions ne sont pas toujours exécutées selon certains scénarios, comme ci-dessus.
EDIT, quelques minutes plus tard:
Oh, et pour répondre pleinement à la question, vous pouvez maintenant $emit
ou $broadcast
votre événement à partir de l'expression ou de la fonction qui est exécutée dans l' ng-element-ready
d'attribut. :) E. g.:
<div data-ng-element-ready="$emit('someEvent')">
...
<div>
MODIFIER, même plus, quelques minutes plus tard:
@satchmorun réponse fonctionne aussi, mais seulement pour la charge initiale. Voici un très utile DONC, la question qui décrit l'ordre des choses sont exécutés, y compris les fonctions de liaison, app.run
, et les autres. Donc, selon votre cas d'utilisation, app.run
pourrait être bon, mais pas pour des éléments spécifiques, auquel cas les fonctions de liaison sont mieux.
EDIT, cinq mois plus tard, le 17 Oct à 8:11 PST:
Cela ne fonctionne pas avec les partiels qui sont chargés de manière asynchrone. Vous aurez besoin d'ajouter de comptabilité dans votre partiels (par exemple, une façon est de faire de chaque partiel de garder une trace de quand son contenu est fait, le chargement puis émet un événement de sorte que le parent peut compter le nombre de partiels ont chargé et enfin faire ce qu'il doit faire après tous les partiels sont chargés).
EDIT, le 23 octobre à 10:52pm PST:
J'ai fait une simple directive pour la cuisson de certains de code lorsque l'image est chargée:
/*
* 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 Oct à 12:48 PST:
J'ai amélioré mon originale ngElementReady
directive et le renomme en 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);
}
}
}
}]);
Par exemple, l'utiliser comme ceci à feu someFunction
lorsqu'un élément est chargé et {{placeholders}}
pas encore remplacé:
<div when-ready="someFunction()">
<span ng-repeat="item in items">{{item.property}}</span>
</div>
someFunction
sera appelée avant tout l' item.property
des espaces réservés sont remplacés.
Évaluer autant d'expressions que vous voulez, et de faire la dernière expression en true
attendre {{placeholders}}
à être évalué comme suit:
<div when-ready="someFunction(); anotherFunction(); true">
<span ng-repeat="item in items">{{item.property}}</span>
</div>
someFunction
et anotherFunction
sera déclenché après l' {{placeholders}}
ont été remplacés.
Cela ne marche que la première fois qu'un élément est chargé, pas sur les évolutions futures. Il peut ne fonctionne pas comme souhaité si un $digest
continue à se produire après des espaces réservés ont d'abord été remplacé ($digest peut se produire jusqu'à 10 fois jusqu'à ce que les données de cesse de changer). Il va être adapté pour une grande majorité de cas d'utilisation.
EDIT, 31 Oct à 7:26pm PST:
Bon, c'est probablement ma dernière mise à jour. Ce sera sans doute pour 99.999 des cas d'utilisation:
/*
* 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: http://stackoverflow.com/questions/14968690/sending-event-when-angular-js-finished-loading
*
* Execute multiple expressions in the when-ready attribute by delimiting them
* with a semi-colon. when-ready="doThis(); doThat()"
*
* Optional: If the value of a wait-for-interpolation attribute on the
* element evaluates to true, then the expressions in when-ready will be
* evaluated after all text nodes in the element have been interpolated (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);
}
}
};
}]);
L'utiliser comme ceci
<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>
Bien sûr, il peut probablement être optimisé, mais je vais en rester là. requestAnimationFrame est agréable.