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

fix(upgrade): fix/improve support for lifecycle hooks #13020

Merged
merged 2 commits into from
Dec 27, 2016

Conversation

gkalpak
Copy link
Member

@gkalpak gkalpak commented Nov 22, 2016

Please check if the PR fulfills these requirements

What kind of change does this PR introduce? (check one with "x")

[x] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:

What is the current behavior? (You can also link to an open issue here)

  • Lifecycle hooks are called on the "binding destination" (either the controller instance or the scope depending on the value of bindToController).
  • The $onInit() lifecycle hook is called after the linking phase.
  • The $postLink() lifecycle hook is not supported.

What is the new behavior?

  • With the exception of $onChanges, all lifecycle hooks are called on the controller instance, regardless of the value of bindToController (as happens in ng1).
  • The $onInit() lifecycle hook is called before the linking phase (as happens in ng1).
  • The $postLink lifecycle hook is supported.

Does this PR introduce a breaking change? (check one with "x")

[ ] Yes
[ ] No
[x] Maybe

If this PR contains a breaking change, please describe the impact and migration path for existing applications:

Theoretically, this is a fix. But if an application was relying on the previous (inconsistent with ng1) behavior and timing (which they shouldn't), it might break.

@gkalpak
Copy link
Member Author

gkalpak commented Nov 22, 2016

BTW, this is a follow-up to #12875.

@chuckjaz
Copy link
Contributor

Please update the commit comments to use "fix(upgrade):" as a prefix instead of "fix(UpgradeNg1Component):"

@gkalpak gkalpak force-pushed the fix-ngUpgrade-jit-lifecycle-hooks branch from 0969a39 to 223870e Compare November 23, 2016 10:06
@gkalpak gkalpak changed the title fix(UpgradeNg1Component): fix/improve support for lifecycle hooks fix(upgrade): fix/improve support for lifecycle hooks Nov 23, 2016
@gkalpak
Copy link
Member Author

gkalpak commented Nov 23, 2016

Rebased (to account for 9092680) and updated commit message scopes.

@vicb
Copy link
Contributor

vicb commented Dec 9, 2016

@gkalpak @petebacondarwin what is the status here ?

@vicb vicb added comp: upgrade/dynamic action: review The PR is still awaiting reviews from at least one requested reviewer labels Dec 9, 2016
@gkalpak
Copy link
Member Author

gkalpak commented Dec 10, 2016

It needs to be reviewed 😃

@@ -313,6 +326,12 @@ class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck {
return controller;
}

private callLifecycleHook(method: LifecycleHook, context: IBindingDestination, arg?: any) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this indirection needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just avoiding repetition.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, because of the if. OK.

const element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect($postLinkSpyA).toHaveBeenCalled();
expect($postLinkSpyB).toHaveBeenCalled();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In many test cases, like this one, it's not actually verified if the hooks are called in a proper moment during the lifecycle of the component, just that they're called. I feel more thorough tests are needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
There is a test for the AoT version that at least verifies the relative order of lifecycle hooks (it is not perfect, but it is better than not having it). Maybe we should port it over and also expand the current tests.
(But this is not directly related to this PR.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order is nice but it still doesn't verify the hooks are called in proper moments (i.e. after all prerequisite operations for calling them have been finished).

But I agree it's not necessarily related to this PR.

@@ -313,6 +326,12 @@ class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck {
return controller;
}

private callLifecycleHook(method: LifecycleHook, context: IBindingDestination, arg?: any) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, because of the if. OK.

Copy link
Member

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is good enough to merge. I left a few minor comments that can be fixed up in subsequent PRs. In particular I think we should have a test that logs all the hook calls to some array so that we can easily see the order of the hooks.

bindToController: false,
controllerAs: '$ctrl',
controller: function() { this.$onInit = $onInitSpyB; }
}))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding here is that you want to test the two scenarios where $onInit is provided on the controller instance and on the controller's prototype. So why does one have bindToController set to true and the other one set to false?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

controller: function($scope: angular.IScope) {
$scope['$onInit'] = $onInitSpy;
}
}))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the test for $onInit on controllers, there seems to be two variables here: on the prototype or not and bind to controller or not. If these are independent then perhaps we need four checks?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right: We have two independent variables (bindToControler: true/false and prototype vs instance). Actually, we have a third one which is scope vs controller. So, ideally we need 8 different components tested per hook (× 5 hooks × 2 (dynamic + static upgrade) = 80 components). I cheated by testing two variables per component to save 50% of components tested.

In an ideal world, where bindToController is indeed independent from prototype vs instance, this should be OK. In the non-ideal world that we live in, we need the 40 extra components (or double as much faith 😛 )

ref.dispose();
});
}));
it('should not call `$onDestroy()` on scope', fakeAsync(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing carriage return here?

@petebacondarwin petebacondarwin added action: merge The PR is ready for merge by the caretaker pr_state: LGTM and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Dec 19, 2016
@gkalpak gkalpak force-pushed the fix-ngUpgrade-jit-lifecycle-hooks branch from 223870e to 2c59924 Compare December 19, 2016 20:37
@gkalpak
Copy link
Member Author

gkalpak commented Dec 19, 2016

(I just added a missing newline to fix comment, hence the rebase.)

@chuckjaz
Copy link
Contributor

@gkalpak Can you rebase this to the current master to resolve conflicts?

@chuckjaz chuckjaz added action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews and removed action: merge The PR is ready for merge by the caretaker labels Dec 20, 2016
@gkalpak gkalpak force-pushed the fix-ngUpgrade-jit-lifecycle-hooks branch 3 times, most recently from 3e1a864 to 08d4c0f Compare December 21, 2016 10:33
@gkalpak
Copy link
Member Author

gkalpak commented Dec 21, 2016

@chuckjaz, rebased.

I added one more fixup commit with the changes I made while resolving conflicts. Let me know if you prefer me to squash it.

@petebacondarwin
Copy link
Member

@gkalpak the extra commit looks good. I think you should squashi it, so that it the merge is as straightforward as possible for the caretaker.

@gkalpak gkalpak force-pushed the fix-ngUpgrade-jit-lifecycle-hooks branch from 08d4c0f to a320b66 Compare December 21, 2016 11:40
@gkalpak
Copy link
Member Author

gkalpak commented Dec 21, 2016

Done.

@chuckjaz chuckjaz added the action: merge The PR is ready for merge by the caretaker label Dec 21, 2016
@chuckjaz chuckjaz removed the action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews label Dec 21, 2016
@@ -313,6 +326,12 @@ class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck {
return controller;
}

private callLifecycleHook(method: LifecycleHook, context: IBindingDestination, arg?: any) {
if (context && typeof context[method] === 'function') {
context[method](arg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style of invocation breaks Closure advanced optimizations and we try to avoid this. Can you change this to avoid the dynamic invocation? I would prefer the code duplication than using dynamic indexing to avoid it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chuckjaz, can you give me some more context? Do the optimizations cause the resulting code to break or does the dynamic invocation prevent the optimizations from being applied?

I would prefer the code duplication than using dynamic indexing to avoid it.

Do you mean remove the callLifecycleHook method altogether and instead "inline" the code wherever the method was called?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it is possible that inside a Closure compiled app the $onInit, for example, method might be renamed to something "smaller" like k. In this case this function would break the behaviour since it would still be trying to call $onInit.

We are not sure if this is the case, and it might be possible to get around it be annotating the function to prevent renaming.

But @chuckjaz is suggesting that it is safer simply to not do this in the first place.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@chuckjaz chuckjaz added action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews and removed action: merge The PR is ready for merge by the caretaker labels Dec 21, 2016
@gkalpak gkalpak force-pushed the fix-ngUpgrade-jit-lifecycle-hooks branch from 32afed2 to 0e34a9b Compare December 21, 2016 23:36
@chuckjaz
Copy link
Contributor

@gkalpak Can you fix the formatting break? Ensure the gulp lint is clean.

With the exception of `$onChanges()`, all lifecycle hooks in ng1 are called on
the controller, regardless if it is the binding destination or not (i.e.
regardless of the value of `bindToController`).

This change makes `upgrade` mimic that behavior when calling lifecycle hooks.

Additionally, calling the `$onInit()` hook has been moved before calling the
linking functions, which also mimics the ng1 behavior.
@gkalpak gkalpak force-pushed the fix-ngUpgrade-jit-lifecycle-hooks branch from 0e34a9b to d67d90e Compare December 22, 2016 09:29
@gkalpak
Copy link
Member Author

gkalpak commented Dec 22, 2016

@chuckjaz, I tend to forget about that 😞
Fixed now.

gkalpak added a commit to gkalpak/angular that referenced this pull request Dec 22, 2016
…mponent`

Get rid of the dynamic invocation style used in `callLifecycleHook()`, which
would break under Closure Compiler's advanced optimizations.
Related to angular#13020 (comment).
@chuckjaz chuckjaz added action: merge The PR is ready for merge by the caretaker and removed action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews labels Dec 22, 2016
@hansl hansl merged commit e5c4e58 into angular:master Dec 27, 2016
@gkalpak gkalpak deleted the fix-ngUpgrade-jit-lifecycle-hooks branch December 28, 2016 10:06
IgorMinar pushed a commit to IgorMinar/angular that referenced this pull request Jan 6, 2017
With the exception of `$onChanges()`, all lifecycle hooks in ng1 are called on
the controller, regardless if it is the binding destination or not (i.e.
regardless of the value of `bindToController`).

This change makes `upgrade` mimic that behavior when calling lifecycle hooks.

Additionally, calling the `$onInit()` hook has been moved before calling the
linking functions, which also mimics the ng1 behavior.
mhevery pushed a commit that referenced this pull request Jan 29, 2017
…mponent` (#13629)

Get rid of the dynamic invocation style used in `callLifecycleHook()`, which
would break under Closure Compiler's advanced optimizations.
Related to #13020 (comment).

PR Close #13629
asnowwolf pushed a commit to asnowwolf/angular that referenced this pull request Aug 11, 2017
…mponent` (angular#13629)

Get rid of the dynamic invocation style used in `callLifecycleHook()`, which
would break under Closure Compiler's advanced optimizations.
Related to angular#13020 (comment).

PR Close angular#13629
juleskremer pushed a commit to juleskremer/angular that referenced this pull request Aug 28, 2017
…mponent` (angular#13629)

Get rid of the dynamic invocation style used in `callLifecycleHook()`, which
would break under Closure Compiler's advanced optimizations.
Related to angular#13020 (comment).

PR Close angular#13629
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 10, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants