ドキュメントの準備ができて、AngularJSコントローラーで関数を実行する方法は?


254

角度コントローラー内に関数があり、この関数をドキュメントで実行できるようにしたいのですが、domが作成されると、angularが関数を実行することに気付きました。

 function myController($scope)
 {
     $scope.init = function()
     {
        // I'd like to run this on document ready
     }

     $scope.init(); // doesn't work, loads my init before the page has completely loaded
 }

誰もが私がこれについてどうやって行くことができるか知っていますか?

回答:


449

このangular.element(document).ready()メソッドを使用して、ドキュメントの準備ができたときにコールバックをアタッチできます。次のように、コントローラにコールバックをアタッチするだけです。

angular.module('MyApp', [])

.controller('MyCtrl', [function() {
    angular.element(document).ready(function () {
        document.getElementById('msg').innerHTML = 'Hello';
    });
}]);

http://jsfiddle.net/jgentes/stwyvq38/1/


64
または、$ document.ready(function(){...})、Angular Docsを使用できます:docs.angularjs.org/api/ng/service/$document
StuR

29
$document使用するには注入する必要があります。angular.elementそのまま使用できます。
nshew 2014

17
使用するには$ documentを注入する必要がありますが、それはdocument要素を参照するための推奨される方法です。必須ではありませんが、テストが簡単になります。
Jason Cox 2014年

10
documentas $documentはangularによって注入可能であり、したがってテストをより簡単にするグローバル変数です。一般に、ドキュメントやウィンドウなどのグローバルJS変数にアクセスするアプリケーションの単体テストは困難です。またmodule.run、コントローラーの代わりにこのコードを配置するのに適した場所だと思います。
lanoxx 14

7
これは機能しません。getElementById呼び出しはnullを返します。
ThePartyTurtle 16

29

この投稿を参照してくださいページの読み込み時に角度コントローラー機能を実行する方法?
高速検索の場合:

// register controller in html
<div data-ng-controller="myCtrl" data-ng-init="init()"></div>

// in controller
$scope.init = function () {
    // check if there is query in url
    // and fire search in case its value is not empty
};

このように、ドキュメントの準備ができるまで待つ必要はありません。


1
after-page-loadを呼び出す必要がある場合はどうなりますか?
リシ2018

22

Angularは、DOMContentLoadedイベント時に、またはその時点でdocument.readyStateが「complete」に設定されている場合、angular.jsスクリプトが評価されるときに自動的に初期化します。この時点で、Angularはアプリケーションルートを指定するng-appディレクティブを探します。

https://docs.angularjs.org/guide/bootstrap

これは、DOMの準備ができた後にコントローラーコードが実行されることを意味します。

したがって、それだけ$scope.init()です。


9
私はそれをやろうとしましたが、奇妙なことに動作しませんでした。私のページの一部がロードされませんでした
foreyez

そして、自動化されたウェブテストを書くときにボタンをクリックするなどのアクションを実行する前に、いつ角度コードが準備ができているかを知る方法は?
akostadinov 2017年

またDOMContentLoaded、スタイルシート、画像、サブフレームの読み込みが完了するのを待たずに、ドキュメントが完全に読み込まれて解析されたときにも発生します。詳細については、DOMContentLoadedイベントとloadイベントの違いを参照してください
georgeawg 2018

22

Angularには、関数の実行を開始するいくつかの時点があります。jQueryのようなものを探しているなら

$(document).ready();

あなたはこの類似物を角度で見ると非常に役立つかもしれません:

$scope.$watch('$viewContentLoaded', function(){
    //do something
});

これは、DOM要素を操作するときに役立ちます。すべてのte要素がロードされた後にのみ実行を開始します。

UPD:上記の内容は、CSSプロパティを変更する場合に機能します。ただし、要素のプロパティ(幅、高さなど)を測定するときに機能しない場合があります。この場合、次のことを試してみてください。

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});

これは、推奨される回答よりも価値があります。$ timeoutを使用せずにこの作業を行いました。
Richie86 2017年

それは私のためにsetTimeout(0)でのみ機能します-たとえば、ng-repeatの下のコンテンツがない場合、この時点ではまだロードされていません
Ignacio Vazquez

2

同様の状況で、ビューが読み込まれた後、およびビュー内の特定のサードパーティコンポーネントが読み込まれ、初期化され、それ自体への参照が$ scopeに配置された後で、コントローラー関数を実行する必要がありました。結局私にとってうまくいったのは、このスコーププロパティに監視を設定し、初期化された後にのみ関数を起動することでした。

// $scope.myGrid property will be created by the grid itself
// The grid will have a loadedRows property once initialized

$scope.$watch('myGrid', function(newValue, oldValue) {
    if (newValue && newValue.loadedRows && !oldValue) {
        initializeAllTheGridThings();
    }
});

ウォッチャーは、未定義の値で数回呼び出されます。次に、グリッドが作成され、予期されたプロパティがある場合、初期化関数を安全に呼び出すことができます。未定義でないnewValueでウォッチャーが初めて呼び出されたとき、oldValueは未定義のままです。


1

これは、coffeescriptを使用した外部コントローラー内での私の試みです。それはかなりうまくいきます。settings.screen.xs | sm | md | lgは、アプリに含まれている非拡張ファイルで定義されている静的な値であることに注意してください。値は、名を冠したメディアクエリサイズのBootstrap 3公式ブレークポイントごとです。

xs = settings.screen.xs // 480
sm = settings.screen.sm // 768
md = settings.screen.md // 992
lg = settings.screen.lg // 1200

doMediaQuery = () ->

    w = angular.element($window).width()

    $scope.xs = w < sm
    $scope.sm = w >= sm and w < md
    $scope.md = w >= md and w < lg
    $scope.lg = w >= lg
    $scope.media =  if $scope.xs 
                        "xs" 
                    else if $scope.sm
                        "sm"
                    else if $scope.md 
                        "md"
                    else 
                        "lg"

$document.ready () -> doMediaQuery()
angular.element($window).bind 'resize', () -> doMediaQuery()

1

答え

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});

私がテストしたほとんどのシナリオで機能するのはこれだけです。テンプレートからHTMLを構築する4つのコンポーネントを持つサンプルページでは、イベントの順序は

$document ready
$onInit
$postLink
(and these 3 were repeated 3 more times in the same order for the other 3 components)
$viewContentLoaded (repeated 3 more times)
$timeout execution (repeated 3 more times)

したがって、$ document.ready()は、angularで構築されているDOMが準備ができていない可能性があるため、ほとんどの場合役に立たない。

しかし、もっと興味深いのは、$ viewContentLoadedが発生した後でも、関心のある要素がまだ見つからなかったことです。

$ timeoutが実行された後にのみ、それが見つかりました。$ timeoutの値が0であっても、実行までに約200ミリ秒が経過しているため、このスレッドがしばらく(おそらくDOMがメインスレッドに角度テンプレートを追加している間)保留されていたことを示しています。最初の$ document.ready()から最後の$ timeoutの実行までの合計時間は、ほぼ500ミリ秒でした。

コンポーネントの値が設定され、text()値が後で$ timeoutで変更された1つの異常なケースでは、$ timeout値は、それが機能するまで増加する必要がありました(ただし、$ timeout中に要素が見つかった場合でも) )。サードパーティコンポーネント内で非同期の何かが発生したため、十分な時間が経過するまで、値がテキストよりも優先されました。別の可能性は$ scope。$ evalAsyncですが、試行されませんでした。

私はまだ、DOMが完全に落ち着いており、すべてのケースが機能するように操作できることを通知する1つのイベントを探しています。これまでのところ、任意のタイムアウト値が必要です。これは、せいぜい遅いブラウザでは機能しない可能性があることを意味します。私はliveQueryやパブリッシュ/サブスクライブのようなJQueryオプションを試したことはありませんが、確かに純粋なものではありません。


1

のようなものを取得している場合getElementById call returns null、それはおそらく関数が実行されているが、IDがDOMにロードする時間がないためです。

遅れてウィルの答えを使用してみてください(上に向かって)。例:

angular.module('MyApp', [])

.controller('MyCtrl', [function() {
    $scope.sleep = (time) => {
        return new Promise((resolve) => setTimeout(resolve, time));
    };
    angular.element(document).ready(function () {
        $scope.sleep(500).then(() => {        
            //code to run here after the delay
        });
    });
}]);

0

角度のドキュメントが言及するものとしようとしないのはなぜhttps://docs.angularjs.org/api/ng/function/angular.elementを

angular.element(コールバック)

私はこれを$ onInit(){...}関数内で使用しました。

 var self = this;

 angular.element(function () {
        var target = document.getElementsByClassName('unitSortingModule');
        target[0].addEventListener("touchstart", self.touchHandler, false);
        ...
    });

これでうまくいきました。


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