domのレンダリングが終了した後でディレクティブを実行するにはどうすればよいですか?


115

(Angular JSのドキュメントを読むことで)明らかな解決策がない一見単純な問題があります。

DOM内のコンテナーの高さを定義するために、他のDOM要素の高さに基づいていくつかの計算を行うAngular JSディレクティブがあります。

これと同様のことがディレクティブ内で行われています。

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

問題は、ディレクティブが実行さ$('site-header')れると見つからず、必要なjQueryでラップされたDOM要素の代わりに空の配列を返すことです。

DOMが読み込まれた後にのみ実行され、通常のjQueryセレクタースタイルのクエリを介して他のDOM要素にアクセスできるディレクティブ内で使用できるコールバックはありますか?


1
scope。$ on()およびscope。$ emit()を使用して、カスタムイベントを使用できます。これが正しい/推奨されるアプローチかどうかはわかりません。
Tosh

回答:


137

$( 'site-header')の構成方法によって異なります。

$ timeoutを遅延なしで使用することができます。何かのようなもの:

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

仕組みの説明:1つ2つ

$timeoutディレクティブに注入することを忘れないでください:

.directive('sticky', function($timeout)

5
おかげで、私$timeoutはディレクティブに合格しなかったことに気付くまで、これを何年にもわたって機能させようとしました。どー。すべてがうまくいきました、乾杯。
Jannis、2012

5
はい、合格する必要があります $timeout。このようにディレクティブに.directive('sticky', function($timeout) { return function (scope, element, attrs, controller) { $timeout(function(){ }); }); };
ウラジミールStarkov

19
リンクされた説明は、タイムアウトトリックがJavaScriptで機能する理由を説明していますが、AngularJSのコンテキストでは機能しません。公式ドキュメントから:「[...] 4. $ evalAsyncキューは、現在のスタックフレームの外側で、ブラウザーのビューがレンダリングされる前に発生する必要がある作業をスケジュールするために使用されます。これは通常、setTimeout(0)行われが、ブラウザが各イベントの後にビューをレンダリングするため、setTimeout(0)アプローチは速度が低下し、ビューのちらつきを引き起こす可能性があります。[...] "(私の強調)
Alberto

12
私は同様の問題に直面しており、ディレクティブを実行する前にDOMをロードできるようにするために約300msを必要とすることがわかりました。私はそのように一見任意の数を差し込むのが本当に好きではありません。DOMの読み込み速度はユーザーによって異なると思います。では、300msが私のアプリを使用しているすべてのユーザーに確実に機能するようにするにはどうすればよいですか?
keepitreal 2014年

5
この答えについてはあまり満足していません。OPの質問に答えているようですが、それは彼らのケースに非常に固有であり、問​​題のより一般的な形式(つまり、domがロードされた後にディレクティブを実行すること)との関連性は明らかではありません+あまりにもハックしすぎです..角度に関しては具体的には何もありません
14年

44

ここに私がそれをする方法があります:

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

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

});

1
要素がすでに提供されていますので、でもangular.elementを必要はありません:element.ready(function(){
timhc22

1
@ timhc22要素は、ディレクティブをトリガーしたDOMElementへの参照です。推奨では、ページDocumentオブジェクトへのDOMElement参照は発生しません。
トビウス2017年

それは正しく動作しません。私はこのアプローチでoffsetWidth = 0を取得しています
Alexey Sh。

37

おそらく著者はもう私の答えを必要としません。それでも、完全を期すために、他のユーザーが役立つと思うかもしれません。最良かつ最も簡単な解決策は$(window).load()、返された関数の本体内で使用することです。(または、使用できますdocument.ready。すべての画像が必要かどうかは実際に異なります)。

使用する $timeout私の謙虚な意見でのは非常に弱いオプションであり、場合によっては失敗することがあります。

これが私が使用する完全なコードです:

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

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

1
「場合によっては失敗する」理由を詳しく説明できますか?どういう意味?
rryter

6
ここではjQueryが利用可能であると想定しています。
ジョナサンクレミン2014年

3
@JonathanCremin jQueryの選択は、OPの目前の問題です
Nick Devereaux

1
これはうまく機能しますが、ディレクティブを使用して新しいアイテムをビルドする投稿がある場合、ウィンドウのロードは初期ロードの後に​​起動しないため、正しく機能しません。
ブライアンスコット

@BrianScott-最初のページレンダリングに$(window).loadの組み合わせを使用し(ユースケースは埋め込みフォントファイルを待っていました)、その後element.readyを使用してビューの切り替えを処理しました。
aaaaaa 2015年

8

ngcontentloadedイベントがあり、利用できると思います

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

       }
   }
});

21
が何をし$ $windowているか説明できますか?
ナマズ2014

2
いくつかの
コーヒースクリプトの

5

外部リソースのために$ timeoutを使用できず、タイミングに関する特定の問題のためにディレクティブを使用できない場合は、ブロードキャストを使用します。

$scope.$broadcast("variable_name_here");必要な外部リソースまたは長期実行コントローラー/ディレクティブが完了した後に追加します。

次に、外部リソースが読み込まれた後に以下を追加します。

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

たとえば、遅延されたHTTPリクエストの約束で。

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

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

2
ロードされたデータは、それらがDOM要素にバインドされた適切なスコープ変数にある場合でも、すでにDOMでレンダリングされていることを意味しないため、これは問題を解決しません。それらがスコープに読み込まれた瞬間とDOMにレンダリングされた出力の間にタイムスパンがあります。
ルネStalder

1

同様の問題があり、私の解決策をここで共有したいと思います。

次のHTMLがあります。

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

問題:親divのディレクティブのリンク関数で、子div#subにjqueryを実行する必要がありました。しかし、ディレクティブのリンク関数が実行されたときにng-includeが終了していなかったため、空のオブジェクトが表示されました。したがって、最初に$ timeoutを使用してダーティーな回避策を作成しましたが、これは機能しましたが、遅延パラメーターはクライアントの速度に依存していました(誰もそれが好きではありません)。

動作しますが汚れています:

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;
}]);

これがクリーンなソリューションです:

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;
}]);

多分それは誰かを助ける。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.