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
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 :wink:).
Thank you very much for you answer, it's clearer now :)
I understand this is intentional, how about if we register definition of $onChanges() function inside $onInit().
@bharatpatil, it should work in AngularJS (1.x.) but won't in Angular (2+).
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?
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()
.
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.
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.
Par for the course with Angular... why would anyone expect init to happen before changes?
Most helpful comment
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 theisFirstChange()
method of any of theSimpleChange
objects:#
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 :wink:).