ビューが読み込まれたときにAngularJS初期化コードを実行する


92

ビューをロードするときに、関連するコントローラーで初期化コードを実行したいと思います。

そのために、ビューのメイン要素でng-initディレクティブを使用しました。

<div ng-init="init()">
  blah
</div>

そしてコントローラーで:

$scope.init = function () {
    if ($routeParams.Id) {
        //get an existing object
        });
    } else {
       //create a new object
    }

    $scope.isSaving = false;
}

最初の質問:これは正しい方法ですか?

次に、発生する一連のイベントに問題があります。ビューには、「保存」ボタンがあります。これは、次のng-disabledようにディレクティブを使用します。

<button ng-click="save()" ng-disabled="isClean()">Save</button>

isClean()関数は、コントローラに定義されています。

$scope.isClean = function () {
    return $scope.hasChanges() && !$scope.isSaving;
}

ご覧のとおり$scope.isSavinginit()関数で初期化されたフラグを使用しています。

問題:ビューが読み込まれると、関数の前に isClean関数が呼び出されるinit()ため、フラグisSavingundefinedです。それを防ぐにはどうすればよいですか?

回答:


136

ビューが読み込まれると、それに関連付けられているコントローラーも読み込まれます。を使用する代わりに、コントローラーでメソッドをng-init呼び出すだけですinit()

$scope.init = function () {
    if ($routeParams.Id) {
        //get an existing object
    } else {
        //create a new object
    }
    $scope.isSaving = false;
}
...
$scope.init();

コントローラはの前ng-initに実行されるため、これにより2番目の問題も解決されます。

フィドル


以下のようJohn David Fiveに述べた、あなたはこれを添付したくない場合があります$scope。このメソッドはプライベートにするために。

var init = function () {
    // do something
}
...
init();

jsFiddleを参照してください


特定のデータがプリセットされるのを待つ場合は、そのデータリクエストをresolveに移動するか、そのコレクションまたはオブジェクトにウォッチャーを追加し、データがinit基準を満たしたときにinitメソッドを呼び出します。私は通常、データ要件が満たされるとウォッチャーを削除するので、監視しているデータが変更され、initメソッドを実行するための基準を満たした場合に、init関数がランダムに再実行されません。

var init = function () {
    // do something
}
...
var unwatch = scope.$watch('myCollecitonOrObject', function(newVal, oldVal){
                    if( newVal && newVal.length > 0) {
                        unwatch();
                        init();
                    }
                });

8
初期化を実行するために一部のモデルのデータが必要な場合はどうなりますか?または、レンダリング時にページ上で利用可能な一部のデータのみを使用して、初期化を機能させることができますか?
ユージーン

38
init関数は$ scopeにアタッチする必要はありません。init関数をプライベートにします。init関数を2回以上実行したくないので、$ scopeで公開しないでください。
John David Five

2
ビューが表示されているときはいつでもinit関数を実行したいのですが、関数が1回だけ実行される方法がわかりません。すべてのページ/テンプレートのロードでそれを実行する方法はありますか?
Jorre、2014年

9
私は角度の専門家ではありませんが、init()は単にコントローラのインスタンス化で呼び出されるため、このアプローチはテストに不向きです...つまり、コントローラの単一のメソッドをテストする必要がある場合、init()も呼び出されます。 。テストを破る!
Wagner Leonardi、2015年

1
@WagnerLeonardiが言ったこと。このアプローチでは、「プライベート」なinit()メソッドのテストが非常に困難になります。
Steven Rogers

36

AngularJS 1.5以降$onInitでは、AngularJSコンポーネントで使用可能なものを使用する必要があります。v1.5以降のコンポーネントライフサイクルドキュメントからの優先:

$ onInit()-要素のすべてのコントローラーが構築され、バインディングが初期化された後(およびこの要素のディレクティブのリンク前後の関数の前)に、各コントローラーで呼び出されます。これは、コントローラーの初期化コードを配置するのに適した場所です。

var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {

    //default state
    $scope.name = '';

    //all your init controller goodness in here
    this.$onInit = function () {
      $scope.name = 'Superhero';
    }
});

>> フィドルデモ


コンポーネントのライフサイクルを使用する高度な例:

コンポーネントのライフサイクルにより、コンポーネントを適切に処理することができます。これにより、コンポーネントの「init」、「change」、「destroy」などのイベントを作成できます。このようにして、コンポーネントのライフサイクルに依存するものを管理できます。この小さな例は、$rootScopeイベントリスナーの登録と登録解除を示しています$on。知ることにより、バインド$onされたイベント$rootScopeは、コントローラーがビュー内での参照を失ったとき、または破棄されたときに解放されず、$rootScope.$onリスナーを手動で破棄する必要があります。そのようなものを置くのに適した場所$onDestroyは、コンポーネントのライフサイクル機能です。

var myApp = angular.module('myApp',[]);

myApp.controller('MyCtrl', function ($scope, $rootScope) {

  var registerScope = null;

  this.$onInit = function () {
    //register rootScope event
    registerScope = $rootScope.$on('someEvent', function(event) {
        console.log("fired");
    });
  }

  this.$onDestroy = function () {
    //unregister rootScope event by calling the return function
    registerScope();
  }
});

>> フィドルデモ


17

または、コントローラーでインラインを初期化することもできます。コントローラーの内部でinit関数を使用する場合は、スコープで定義する必要はありません。実際、それは自己実行することができます:

function MyCtrl($scope) {
    $scope.isSaving = false;

    (function() {  // init
        if (true) { // $routeParams.Id) {
            //get an existing object
        } else {
            //create a new object
        }
    })()

    $scope.isClean = function () {
       return $scope.hasChanges() && !$scope.isSaving;
    }

    $scope.hasChanges = function() { return false }
}

1
initコードが匿名で閉じられている理由はありますか?
Adam Tolley、2015

@AdamTolley特別な理由はありません。彼は関数を定義し、変数にバインドすることなく、すぐにそれを呼び出します。
Tair

7
このようにして、プライベートinit()関数を適切にユニットテストできますか?
Steven Rogers

パブリックメンバーのみが単体テストされます。単体テストは、期待される結果を得るためにクラスが非公開で行うことに依存すべきではありません。
Phil

14

私のプロジェクトでは次のテンプレートを使用しています。

angular.module("AppName.moduleName", [])

/**
 * @ngdoc controller
 * @name  AppName.moduleName:ControllerNameController
 * @description Describe what the controller is responsible for.
 **/
    .controller("ControllerNameController", function (dependencies) {

        /* type */ $scope.modelName = null;
        /* type */ $scope.modelName.modelProperty1 = null;
        /* type */ $scope.modelName.modelPropertyX = null;

        /* type */ var privateVariable1 = null;
        /* type */ var privateVariableX = null;

        (function init() {
            // load data, init scope, etc.
        })();

        $scope.modelName.publicFunction1 = function () /* -> type  */ {
            // ...
        };

        $scope.modelName.publicFunctionX = function () /* -> type  */ {
            // ...
        };

        function privateFunction1() /* -> type  */ {
            // ...
        }

        function privateFunctionX() /* -> type  */ {
            // ...
        }

    });

これは見た目はきれいですが、iffeは、スコープで定義しているメソッドの実行を妨げます。これは、多くの場合、私たちが行う必要があることであり、起動時に一度実行してから、それらを実行できるようにスコープでも実行します。ユーザーの必要に応じて、再度
chrismarx 2015

つまり、それがコントローラーの上部で実行されている場合
chrismarx
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.