What Is the Lifecycle of an Angularjs Controller

What is the lifecycle of an AngularJS Controller?

Well, actually the question is what is the life cycle for a ngView controller.

Controllers are not singletons. Anyone can create a new controller and they are never auto-destroyed. The fact is that it's generally bound to the life cycle of its underlying scope. The controller is not automatically destroyed whenever its scope is destroyed. However, after destroying an underlying scope, its controller is useless (at least, by design, it should be).

Answering your specific question, a ngView directive (as well for ngController directive) will always create a new controller and a new scope every time a navigation happens. And the last scope is going to be destroyed as well.

The life cycle "events" are quite simple. Your "creation event" is the construction of your controller itself. Just run your code. To know when it gets useless ("destruction event"), listen to scope $destroy event:

$scope.$on('$destroy', function iVeBeenDismissed() {
// say goodbye to your controller here
// release resources, cancel request...
})

For ngView specifically, you are able to know when the content gets loaded through the scope event $viewContentLoaded:

$scope.$on('$viewContentLoaded', function readyToTrick() {
// say hello to your new content here
// BUT NEVER TOUCHES THE DOM FROM A CONTROLLER
});

Here is a Plunker with a concept proof (open your console window).

Lifecycle of angular controller

It is expected behaviour in AngularJS, {{}}(interpolation) directive will get call on each digest cycle and evaluates there expression. Like interpolation directive most of the angular directive gets evaluated when digest cycle run eg. ng-bind, ng-show, ng-class, ng-if, etc.

If you want to execute you binding code only once then you need to use bindonce directive which :: & your code would be

<div ng-controller="MyCtrl">
{{::logToConsole()}}
</div>

Detailed explaination How Binding work in Angularjs?

AngularJS pure ng-controller lifecycle hooks

According to the section Scope Life Cycle in the scope documentation (v1.6.10) there are not such hooks (using the ng-controller approach).

The scope lifecycle would go like this:

  1. Creation

The root scope is created during the application bootstrap by the $injector. During template linking, some directives create new child scopes.


  1. Watcher registration

During template linking, directives register watches on the scope. These watches will be used to propagate model values to the DOM.


  1. Model mutation

For mutations to be properly observed, you should make them only within the scope.$apply(). AngularJS APIs do this implicitly, so no extra $apply call is needed when doing synchronous work in controllers, or asynchronous work with $http, $timeout or $interval services.


  1. Mutation observation

At the end of $apply, AngularJS performs a $digest cycle on the root scope, which then propagates throughout all child scopes. During the $digest cycle, all $watched expressions or functions are checked for model mutation and if a mutation is detected, the $watch listener is called.


  1. Scope destruction

When child scopes are no longer needed, it is the responsibility of the child scope creator to destroy them via scope.$destroy() API. This will stop propagation of $digest calls into the child scope and allow for memory used by the child scope models to be reclaimed by the garbage collector.


Of course, alternatively, you can always use $rootScope.Scope#$on for listening for changes.

Examples:

$scope.$on('my-custom-event', function () {
// some code to execute when my-custom-event is fired
});

$scope.$on('$destroy', function () {
// some code to execute when the scope is destroyed
});

When to use the AngularJS `$onInit` Life-Cycle Hook

Code has to be moved in the $onInit function, when it depends on bindings, because these bindings are not available within this in the constructor. They get assigned AFTER instantiation of the component class.

Example:
You have a state definition like this:

$stateProvider.state("app", {
url: "/",
views: {
"indexView": {
component: "category"
}
},
resolve: {
myResolve: (someService) => {
return someService.getData();
}
}
});

You can bind the result of myResolve to your component like this:

export const CategoryComponent = {
bindings: {
myResolve: "<"
},
controller: Category
};

If you now log out this.myResolve in the constructor and in $onInit you will see something like this:

constructor() {
console.log(this.myResolve); // <-- undefined
}

$onInit() {
console.log(this.myResolve); // <-- result of your resolve
}

So, your constructor should only contain constructing code like:

constructor() {
this.myArray = [];
this.myString = "";
}

Every angular specific initialisation and binding or dependency usage should be in $onInit

AngularJS directive/controller lifecycle and unbinding $watch and $on listeners

Angular handles that for you.

When scope is destroyed (for example when new view is loaded via ng-view directive old view's scope is destroyed ) all it's child scopes are destroyed and theirs $watchers and listeners registered via $on as well.

$rootScope isn't destroyed at all during lifetime of your application, so you have to manages it's listeners manually, but generally you register there stuff which should be permanent.

When you register listeners via addEventListener you have to remove them manually as it's not managed via angular.

What is the Angular ui-router lifecycle? (for debugging silent errors)

After some experimenting, I figured out how to see into the lifecycle well enough to debug my app and get a feel for what was happening. Using all the events, including onEnter, onExit, stateChangeSuccess, viewContentLoaded from here, gave me a decent picture of when things are happening in a way that's more both more flexible and specific to my code than a written out lifecycle. In the app module's "run" function, I placed:

This code would have saved me days of time and confusion if I started using it when I first started with Angular and UI-router. UI-router needs a "debug" mode that enables this by default.

$rootScope.$on('$stateChangeStart',function(event, toState, toParams, fromState, fromParams){
console.log('$stateChangeStart to '+toState.name+'- fired when the transition begins. toState,toParams : \n',toState, toParams);
});
$rootScope.$on('$stateChangeError',function(event, toState, toParams, fromState, fromParams, error){
console.log('$stateChangeError - fired when an error occurs during transition.');
console.log(arguments);
});
$rootScope.$on('$stateChangeSuccess',function(event, toState, toParams, fromState, fromParams){
console.log('$stateChangeSuccess to '+toState.name+'- fired once the state transition is complete.');
});
$rootScope.$on('$viewContentLoading',function(event, viewConfig){
console.log('$viewContentLoading - view begins loading - dom not rendered',viewConfig);
});

/* $rootScope.$on('$viewContentLoaded',function(event){
// runs on individual scopes, so putting it in "run" doesn't work.
console.log('$viewContentLoaded - fired after dom rendered',event);
}); */

$rootScope.$on('$stateNotFound',function(event, unfoundState, fromState, fromParams){
console.log('$stateNotFound '+unfoundState.to+' - fired when a state cannot be found by its name.');
console.log(unfoundState, fromState, fromParams);
});

AngularJS initialize controller variables

Controller lifecycle hooks were introduced in AngularJS 1.5. $onInit hook is supposed to play exactly this role:

this.$onInit = function () { ... }

$onInit hook is a replacement for pre-link function. It is executed by the compiler.
It may not be executed if a controller doesn't belong to a directive (ng-controller is a directive, too) but is instantiated directly with $controller, like route controller. In this case this.$onInit() should be called explicitly in constructor.

It should be noticed that in original snippet function init() {...} doesn't play significant role. It isn't exposed as a method, so it cannot help a controller to be more testable or extensible.



Related Topics



Leave a reply



Submit