web-dev-qa-db-ja.com

AngularJSはDOMの変更を監視します

リンクされた要素のchildrenを反復処理するauto-carouselディレクティブが1つあります。

ただし、ng-ifs式はまだ解析されていないため、子はまだDOMにロードされていません。

親ディレクティブがそのDOMツリーに変更があったことを確実に確認するにはどうすればよいですか?

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

$timeoutを使用できますが、それは信頼できないと感じています。 ng-showの代わりにng-ifを使用することもできますが、それは質問には答えず、必要なものにも答えません。

34
pilau

だからここに私がやったことです:

関数を$scope.$watchに渡すことができることを発見しました。そこから、変更を監視する式の値を返すのは非常に簡単です。これは、スコープのプロパティにキー文字列を渡すのとまったく同じように機能します。

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

childNodesリストには要素およびが含まれているため、childrenではなくchildNodesを監視しています。テキストノードとコメント。 Angularは、トランスクルージョンを実行してDOMを変更するng-repeatng-ifng-switch、およびng-includeなどのディレクティブにコメントプレースホルダーを使用するため、非常に価値があります。 、childrenは要素のみを保持します。

74
pilau

要素のdomのより深い変更を監視する必要がある場合は、MutationObserverを使用します。

.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

このためのディレクティブモジュールを作成しました angular-dom-events

あなたの場合

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

現在サポートしているのはdom-on-createおよびdom-on-destroyが、$ watchコールバックを繰り返しチェックするのではなく、domイベントごとに1回だけ起動するため、受け入れられた回答よりもパフォーマンスが向上しています。

5
ilovett

角度の推奨事項ではないと思いますが、要素の初期化時に起動するng-initを使用できます:

<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

最初にリンク関数内でディレクティブの内容をコンパイルしてみてください。例えば:

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