Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Controllers hooks order ($onChanges called before $onInit) #15515

Closed
clemgrim opened this issue Dec 16, 2016 · 9 comments
Closed

Controllers hooks order ($onChanges called before $onInit) #15515

clemgrim opened this issue Dec 16, 2016 · 9 comments

Comments

@clemgrim
Copy link

Do you want to request a feature or report a bug?
I dont know if it's wanted or if it's a bug

What is the current behavior?
First $onChanges call is done before the $onInit one.
With angular 1.6 default configuration (preassign = false), it's prefearable to initialize controller states in the $onInit function, given the bindings are not accessible yet in the constructor.

What is the expected behavior?
I think it would be more logical to call $onInit first.
Angular shouldnt do anything while the controller is not fully initialized.

What is the motivation / use case for changing the behavior?
If we use objects that are supposed to be created in the $onInit function in $onChanges function; we'll have an error in the first call, because the controller has not been initialized yet.
A solution could be to initialize those objects inside the constructor, but we'll initialize the controller in two different places...

Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.
angular 1.6 version

@clemgrim clemgrim changed the title Controllers hooks order ($onChanges calles before $onInit) Controllers hooks order ($onChanges calls before $onInit) Dec 16, 2016
@clemgrim clemgrim changed the title Controllers hooks order ($onChanges calls before $onInit) Controllers hooks order ($onChanges called before $onInit) Dec 16, 2016
@gkalpak
Copy link
Member

gkalpak commented Dec 16, 2016

This is intended (mainly in order to match the Angular 2+ behavior). In case you have missed it, you can check whether it is the first (pre-$onInit) call of $onChanges, by checking the return value of the isFirstChange() method of any of the SimpleChange objects:

{
  ...
  bindings: {foo: '<'},
  controller: function SomeController() {
    this.$onChanges = function(changes) {
      if (changes.foo.isFirstChange()) {
        // `$onInit()` has not been called yet...
      }
    };
  }
}

I admit that my first reaction was the same. From a mental model perspective it is easier to think $onInit() is the first thing that happens in the controller; then $onChanges(), $onChanges(), $onChanges() and finally $onDestroy().

But if you think about it, the bindings need to be evaluated and assigned to the controller instance before calling $onInit. And by doing so, a change in the (previously undefined) values is detected, which in turn needs to be reported via $onChanges.

Closing, since this is working as expected (or at least as intended 😉).

@clemgrim
Copy link
Author

Thank you very much for you answer, it's clearer now :)

@bharatpatil
Copy link

bharatpatil commented Jun 26, 2017

I understand this is intentional, how about if we register definition of $onChanges() function inside $onInit().

@gkalpak
Copy link
Member

gkalpak commented Jun 29, 2017

@bharatpatil, it should work in AngularJS (1.x.) but won't in Angular (2+).

@ismailarilik
Copy link

if (changes.foo.isFirstChange()) {
// $onInit() has not been called yet...
}

@gkalpak I think this argument is wrong. binding.isFirstChange() method only guarantees that related binding was called the first time. Think a binding which is initialized after a service call takes too long time. In this situation controller.$onInit method should be called before controller.$onChanges method. Am I wrong?

@gkalpak
Copy link
Member

gkalpak commented Apr 27, 2018

A binding always has a value at the beginning (could be undefined), which is always different that its pre-initialized value. Thus, $onChanges() will always be called with changes.foo before calling $onInit().

@toxaq
Copy link

toxaq commented May 20, 2018

It does not make logical sense that a binding can have a value before it has been initialised. That's the dictionary definition of the word initialise (set initial values). You cannot change something if it didn't have an initial value. I think the naming is all wrong here and unnatural.

@jjamid
Copy link

jjamid commented Aug 11, 2019

I can't remember a situation where I wasn't forced to write something like this code:

$onChanges(changesObj: { [index: string]: angular.IChangesObject; })  {
   if (!this.isInitialized) return;
   ...
}

Calling "changes" before "initialization" doesn't make any sense.

@mpnewcomb
Copy link

Par for the course with Angular... why would anyone expect init to happen before changes?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants