加载视图时运行 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;
}

第一个问题,这样做对吗?

接下来,我对事件发生的顺序产生了疑问。在视图中,我有一个‘ save’按钮,它使用了 ng-disabled指令:

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

isClean()函数在控制器中定义:

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

如您所见,它使用了在 init()函数中初始化的 $scope.isSaving标志。

问题: 当加载视图时,isClean 函数被称为 之前init()函数,因此标志 isSavingundefined。我该怎么做才能避免这种情况?

263539 次浏览

When your view loads, so does its associated controller. Instead of using ng-init, simply call your init() method in your controller:

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

Since your controller runs before ng-init, this also solves your second issue.

Fiddle


As John David Five mentioned, you might not want to attach this to $scope in order to make this method private.

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

See jsFiddle


If you want to wait for certain data to be preset, either move that data request to a resolve or add a watcher to that collection or object and call your init method when your data meets your init criteria. I usually remove the watcher once my data requirements are met so the init function doesnt randomly re-run if the data your watching changes and meets your criteria to run your init method.

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

Or you can just initialize inline in the controller. If you use an init function internal to the controller, it doesn't need to be defined in the scope. In fact, it can be self executing:

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 }
}

I use the following template in my projects:

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


});

Since AngularJS 1.5 we should use $onInit which is available on any AngularJS component. Taken from the component lifecycle documentation since v1.5 its the preferred way:

$onInit() - Called on each controller after all the controllers on an element have been constructed and had their bindings initialized (and before the pre & post linking functions for the directives on this element). This is a good place to put initialization code for your controller.

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

Fiddle Demo

An advanced example of using component lifecycle:

The component lifecycle gives us the ability to handle component stuff in a good way. It allows us to create events for e.g. "init", "change" or "destroy" of an component. In that way we are able to manage stuff which is depending on the lifecycle of an component. This little example shows to register & unregister an $rootScope event listener $on. By knowing, that an event $on bound on $rootScope will not be unbound when the controller loses its reference in the view or getting destroyed we need to destroy a $rootScope.$on listener manually.

A good place to put that stuff is $onDestroy lifecycle function of an component:

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

Fiddle demo