Запуск кода инициализации AngularJS при загрузке представления

Когда я загружаю представление, я хотел бы запустить некоторый код инициализации в соответствующем контроллере.

Для этого я использовал директиву 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.isSaving, который был инициализирован в функции init().

ПРОБЛЕМА: при загрузке представления функция isClean называется до функцией init(), поэтому флаг isSaving равен undefined. Что я могу сделать, чтобы это предотвратить?

Ответ 1

Когда ваш просмотр загружается, также связан с ним соответствующий контроллер. Вместо использования 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, это также решает вашу вторую проблему.

Fiddle


Как упоминалось John David Five, вы можете не захотеть присоединить это к $scope, чтобы сделать этот метод закрытым.

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

См. jsFiddle


Если вы хотите дождаться, когда определенные данные будут предварительно настроены, переместите этот запрос данных на разрешение или добавьте наблюдателя в эту коллекцию или объект и вызовите метод init, когда ваши данные соответствуют вашим критериям init. Обычно я удаляю наблюдателя, как только мои требования к данным удовлетворяются, поэтому функция init не выполняет случайного повторного запуска, если данные, которые вы наблюдаете, изменяются и соответствуют вашим критериям для запуска метода init.

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

Ответ 2

Начиная с AngularJS 1.5, мы должны использовать $onInit который доступен на любом компоненте AngularJS. Взято из документации по жизненному циклу компонента, так как v1.5 ее предпочтительным способом:

$ onInit() - вызывается на каждом контроллере после того, как все контроллеры на элементе были созданы и инициализированы их привязки (и до функций привязки pre & post для директив этого элемента). Это хорошее место для ввода кода инициализации для вашего контроллера.

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 регистрацию $rootScope event listener $on. Зная, что событие $on on $rootScope on $rootScope не будет отменено, когда контроллер потеряет свою ссылку в представлении или будет уничтожен, нам нужно вручную уничтожить $rootScope.$on listener. Хорошим местом для размещения этого материала является $onDestroy жизненного цикла $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();
  }
});

>> Скриншот демо

Ответ 3

Или вы можете просто инициализировать встроенный контроллер. Если вы используете функцию 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 }
}

Ответ 4

В моих проектах я использую следующий шаблон:

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  */ {
            // ...
        }

    });