web-dev-qa-db-fra.com

AngularJS regarder DOM changer

J'ai une directive auto-carousel Qui itère à travers les éléments liés children.

Cependant, les enfants ne sont pas encore chargés dans le DOM, car leurs expressions ng-if N'ont pas encore été analysées.

Comment puis-je m'assurer que la directive parent sait qu'il y a eu des changements dans son arborescence DOM?

        <ul class="unstyled" auto-carousel>
          <li class="slide" ng-if="name">{{name}}</li>
          ...
          <li class="slide" ng-if="email">{{email}}</li>
        </ul>

Je pourrais utiliser $timeout Mais cela semble peu fiable. Je pourrais également utiliser ng-show Au lieu de ng-if Mais cela ne répond pas à la question et pas à ce dont j'ai besoin.

34
pilau

Voici donc ce que j'ai fini par faire:

J'ai découvert que vous pouviez transmettre une fonction à $scope.$watch. À partir de là, il est assez simple de renvoyer la valeur de l'expression que vous souhaitez surveiller pour les modifications. Cela fonctionnera exactement comme passer une chaîne de clé pour une propriété sur la portée.

link: function ($scope, $el, $attrs) {
  $scope.$watch(
    function () { return $el[0].childNodes.length; },
    function (newValue, oldValue) {
      if (newValue !== oldValue) {
        // code goes here
      }
    }
  );
}

Je regarde childNodes, pas children, car la liste childNodes contient des éléments ainsi que nœuds de texte et commentaires. Cela n'a pas de prix car Angular utilise des espaces réservés de commentaire pour des directives comme ng-repeat, ng-if, ng-switch et ng-include qui effectue la transclusion et modifie le DOM, tandis que children ne contient que des éléments.

74
pilau

Si vous devez surveiller les changements plus profonds dans le dom de l'élément, MutationObserver est le chemin à parcourir:

.directive('myDirective', function() {
    return {
        ...
        link: function(scope, element, attrs) {
            var observer = new MutationObserver(function(mutations) {
                // your code here ...
            });
            observer.observe(element[0], {
                childList: true,
                subtree: true
            });
        }
    };
});
18
aaaaahaaaaa

J'ai créé un module de directive pour cela angular-dom-events

Dans votre cas, vous pourriez

    <ul class="unstyled" auto-carousel>
      <li class="slide" ng-if="name" dom-on-create="nameCreated()">{{name}}</li>
      <li class="slide" ng-if="email" dom-on-destroy="emailDestroyed()">{{email}}</li>
    </ul>

Actuellement, ne prend en charge que dom-on-create et dom-on-destroy, mais a de meilleures performances que la réponse acceptée car elle ne se déclenchera qu'une seule fois pour chaque événement dom, plutôt que de vérifier à plusieurs reprises le rappel $ watch.

5
ilovett

Bien que je ne pense pas que ce soit avec les recommandations d'angular, vous pouvez utiliser ng-init qui se déclenche lors de l'initialisation de l'élément:

<ul class="unstyled" auto-carousel>
    <li class="slide" ng-if="name" ng-init="recheck()">{{name}}</li>
    <li class="slide" ng-if="email" ng-init="recheck()">{{email}}</li>
</ul>
1
Jason

Vous pouvez essayer de compiler le contenu de la directive d'abord dans votre fonction de lien. Par exemple:

angular.module('myApp').directive('autoCarousel', ['$compile', function ($compile) {

    return {
        templateUrl: 'views/auto-carousel.html',
        restrict: 'A',
        replace: true,
        link: function (scope, element, attr) {
            $compile(element.contents())(scope);

            // your code goes here
        }
    }
}]);
0
SergeL