web-dev-qa-db-fra.com

Comment puis-je exécuter une directive après que le dom ait fini de rendre?

J'ai un problème apparemment simple sans apparente (en lisant la Angular JS docs).

J'ai une directive JS Angular qui effectue des calculs en fonction de la hauteur d'autres éléments du DOM afin de définir la hauteur d'un conteneur dans le DOM.

Quelque chose de semblable à cela se passe dans la directive:

return function(scope, element, attrs) {
    $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
}

Le problème est que lorsque la directive est exécutée, il est impossible de trouver $('site-header'), ce qui renvoie un tableau vide au lieu de l'élément DOM encapsulé jQuery dont j'ai besoin.

Existe-t-il un rappel que je peux utiliser dans ma directive et qui s'exécute uniquement après le chargement du DOM et auquel je peux accéder à d'autres éléments du DOM via les requêtes de style de sélecteur jQuery normales?

114
Jannis

Cela dépend de la façon dont votre $ ('site-header') est construit.

Vous pouvez essayer d'utiliser $ timeout avec 0 délai. Quelque chose comme:

return function(scope, element, attrs) {
    $timeout(function(){
        $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
    });        
}

Explications comment cela fonctionne: n , deux .

N'oubliez pas d'injecter $timeout dans votre directive:

.directive('sticky', function($timeout)
137
Artem Andreev

Voici comment je le fais:

app.directive('example', function() {

    return function(scope, element, attrs) {
        angular.element(document).ready(function() {
                //MANIPULATE THE DOM
        });
    };

});
43
rjm226

L'auteur n'aura probablement plus besoin de ma réponse. Néanmoins, par souci d'exhaustivité, j'estime que d'autres utilisateurs pourraient le trouver utile. La solution la meilleure et la plus simple consiste à utiliser $(window).load() dans le corps de la fonction renvoyée. (Vous pouvez également utiliser document.ready. Cela dépend si vous avez besoin de toutes les images ou non).

Utiliser $timeout à mon humble avis est une option très faible et peut échouer dans certains cas.

Voici le code complet que j'utiliserais:

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function($scope, $elem, attrs){

           $(window).load(function() {
               //...JS here...
           });
       }
   }
});
37
jnardiello

il y a un événement ngcontentloaded, je pense que vous pouvez l'utiliser

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function(scope, elem, attrs){

                $$window = $ $window


                init = function(){
                    contentHeight = elem.outerHeight()
                    //do the things
                }

                $$window.on('ngcontentloaded',init)

       }
   }
});
8
sunderls

Si vous ne pouvez pas utiliser $ timeout en raison de ressources externes et si vous ne pouvez pas utiliser une directive en raison d'un problème spécifique de synchronisation, utilisez broadcast.

Ajoutez $scope.$broadcast("variable_name_here"); une fois que la ressource externe souhaitée ou le contrôleur/la directive ayant fonctionné longtemps est terminé.

Ajoutez ensuite ce qui suit après le chargement de votre ressource externe.

$scope.$on("variable_name_here", function(){ 
   // DOM manipulation here
   jQuery('selector').height(); 
}

Par exemple dans la promesse d'une requête HTTP différée.

MyHttpService.then(function(data){
   $scope.MyHttpReturnedImage = data.image;
   $scope.$broadcast("imageLoaded");
});

$scope.$on("imageLoaded", function(){ 
   jQuery('img').height(80).width(80); 
}
5
JSV

J'ai eu un problème similaire et je veux partager ma solution ici.

J'ai le code HTML suivant:

<div data-my-directive>
  <div id='sub' ng-include='includedFile.htm'></div>
</div>

Problème: Dans la fonction de lien de la directive parent, je voulais interroger l’enfant div # sub. Mais cela m’a juste donné un objet vide car ng-include n’avait pas fini quand la fonction link de la directive s’exécutait. Alors d’abord, j’ai fait une solution de contournement avec $ timeout, qui a fonctionné, mais le paramètre delay a dépendu de la vitesse du client (personne n’aime ça).

Fonctionne mais sale:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        $timeout(function() {
            //very dirty cause of client-depending varying delay time 
            $('#sub').css(/*whatever*/);
        }, 350);
    };
    return directive;
}]);

Voici la solution propre:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        scope.$on('$includeContentLoaded', function() {
            //just happens in the moment when ng-included finished
            $('#sub').css(/*whatever*/);
        };
    };
    return directive;
}]);

Peut-être que cela aide quelqu'un.

1
jaheraho