Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

@clemgrim clemgrim commented Dec 16, 2016

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

This comment has been minimized.

Copy link
Member

@gkalpak 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

This comment has been minimized.

Copy link
Author

@clemgrim clemgrim commented Dec 16, 2016

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

@bharatpatil

This comment has been minimized.

Copy link

@bharatpatil bharatpatil commented Jun 26, 2017

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

@gkalpak

This comment has been minimized.

Copy link
Member

@gkalpak gkalpak commented Jun 29, 2017

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

@ismailarilik

This comment has been minimized.

Copy link

@ismailarilik ismailarilik commented Apr 27, 2018

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

This comment has been minimized.

Copy link
Member

@gkalpak 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

This comment has been minimized.

Copy link

@toxaq 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

This comment has been minimized.

Copy link

@jjamid 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

This comment has been minimized.

Copy link

@mpnewcomb mpnewcomb commented Sep 26, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can’t perform that action at this time.