Запуск коду ініціалізації 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.isSavingпрапор, який був ініціалізований у init()функції.

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

Відповіді:


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, це також вирішує вашу другу проблему.

Скрипка


Як 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();
                    }
                });

8
Що робити, якщо вам потрібні дані деяких моделей для запуску ініціалізації? Або просто деякі дані, доступні на сторінці під час рендерингу, щоб ініціалізація могла працювати?
Євген

38
Функцію init не потрібно приєднувати до $ scope. Зробіть свою функцію ініціювання приватною. Ви ніколи не хочете, щоб функція init запускалася більше одного разу, тому не виставляйте її на $ scope.
Джон Девід П'ять

2
Я хотів би запускати функцію init будь-коли, коли відображається мій погляд, але я не маю уявлення, як, ця функція запускається лише один раз. Будь-які ідеї, як я можу запустити його на кожній сторінці / завантаженні шаблону?
Хорре,

9
Я не експерт з кутових питань, але такий підхід відмовляє від тестування, оскільки init () викликається при простому встановленні контролера ... іншими словами, коли потрібно протестувати один метод контролера, також викликається init (). .розбиття тестів!
Вагнер Леонарді

1
Що сказав @WagnerLeonardi. Цей підхід ускладнює тестування вашого "приватного" методу init ().
Стівен Роджерс

36

Оскільки AngularJS 1.5 ми повинні використовувати той,$onInit який доступний для будь-якого компонента AngularJS. Взято з документації життєвого циклу компонента, починаючи з версії 1.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", "зміни" або "знищення" компонента. Таким чином ми можемо управляти речами, які залежать від життєвого циклу компонента. Цей невеликий приклад показує реєстрацію та скасування реєстрації $rootScopeслухача події $on. Знаючи, що подія $onпереплетено на $rootScopeНЕ буде undinded , коли контролер втрачає свою посилання у вікні або отримати знищено , ми повинні знищити $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-код знаходиться в анонімному закритті?
Адам Толлі,

@AdamTolley немає особливої ​​причини. Він просто визначає функцію і негайно викликає її, не прив'язуючи до змінної.
Таїр

7
Як ви можете таким чином модульно протестувати функцію private init ()?
Стівен Роджерс

Одиничним тестуванням підлягають лише громадські члени. Одиничні тести не повинні залежати від того, які класи проводять приватно, щоб отримати очікувані результати.
Філ

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

тобто якщо він запущений у верхній частині контролера
chrismarx
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.