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(core): QueryList does not fire changes if the underlying list did not change. #40091

Closed
wants to merge 2 commits into from

Conversation

mhevery
Copy link
Contributor

@mhevery mhevery commented Dec 11, 2020

Previous implementation would fire changes QueryList.changes.subscribe
even if underlying list did not change. Such situation can arise if a
LView is inserted or removed, causing QueryList to recompute, but
the recompute results in the same exact QueryList result. In such
a case we don't want to fire a change event (as there is no change.)

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.io application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

@google-cla google-cla bot added the cla: yes label Dec 11, 2020
@pullapprove pullapprove bot added the area: core Issues related to the framework runtime label Dec 11, 2020
@ngbot ngbot bot added this to the Backlog milestone Dec 11, 2020
@mhevery
Copy link
Contributor Author

mhevery commented Dec 11, 2020

presubmit

@mhevery mhevery force-pushed the query_stable_notification branch 2 times, most recently from 77480e0 to f02babb Compare December 11, 2020 20:41
@mhevery
Copy link
Contributor Author

mhevery commented Dec 11, 2020

presubmit ( deflake )

@mhevery mhevery force-pushed the query_stable_notification branch 2 times, most recently from 2cd900f to 266fa09 Compare December 11, 2020 23:35
Copy link
Contributor

@IgorMinar IgorMinar left a comment

Choose a reason for hiding this comment

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

Overall this looks good, thanks for debugging this.

Could you instead consider using the native Array#flat as this would simplify the code and in fact remove bulk of it.

We'll then also need to add

import 'core-js/modules/es.array.flat';
import 'core-js/modules/es.array.flat-map';

to

https://github.com/angular/angular-cli/blob/0d10de5cbba9f4eaf91e0fbc279a1f3e0ad94fa3/packages/angular_devkit/build_angular/src/webpack/es5-polyfills.js#L36-L41

just so that we don't break on IE11 (the flat-map addition is not required by this change, but we might as well add it for future use).

If we target this fix for target: minor then we can roll it out now since CLI would add the polyfill to ie11 builds in v11.1 as well.

packages/core/src/linker/query_list.ts Outdated Show resolved Hide resolved
packages/core/src/util/array_utils.ts Outdated Show resolved Hide resolved
packages/core/src/util/array_utils.ts Outdated Show resolved Hide resolved
packages/core/test/util/array_utils_spec.ts Outdated Show resolved Hide resolved
packages/core/test/util/array_utils_spec.ts Outdated Show resolved Hide resolved
@pullapprove pullapprove bot requested a review from IgorMinar December 12, 2020 00:14
@mhevery mhevery added the target: patch This PR is targeted for the next patch release label Dec 12, 2020
Copy link
Contributor

@AndrewKushnir AndrewKushnir left a comment

Choose a reason for hiding this comment

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

LGTM (from code perspective), thanks for the fix @mhevery 👍

I was also thinking of using Array.flat function as @IgorMinar proposed, but that'd result in additional array allocation and extra pass to compare current/new arrays (which is avoided in the current implementation). Given that QueryList.reset is called for each CD run (executed from refreshView fn), it looks like that might sensitive from perf perspective...

packages/core/src/util/array_utils.ts Outdated Show resolved Hide resolved
packages/core/test/acceptance/query_spec.ts Show resolved Hide resolved
packages/core/test/acceptance/query_spec.ts Outdated Show resolved Hide resolved
@mhevery
Copy link
Contributor Author

mhevery commented Dec 14, 2020

@IgorMinar Array#flat seems like a lot of work, so I am going to do it in a separate PR since it includes changes to polyfills and I don't think we should be changing polyfills in patch release.

@mhevery mhevery force-pushed the query_stable_notification branch 3 times, most recently from 29aa5b4 to 278dbce Compare December 14, 2020 23:56
Comment on lines 203 to 186
(this.changes as EventEmitter<any>).complete();
(this.changes as EventEmitter<any>).unsubscribe();
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably do that for both _changesDeprecated and _changesStrict event emitters (in case both emitters were used in an app).

Comment on lines 178 to 180
(this as {length: number}).length = this._results.length;
(this as {last: T}).last = this._results[this.length - 1];
(this as {first: T}).first = this._results[0];
Copy link
Contributor

Choose a reason for hiding this comment

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

This is outside of the context of this PR, but it feels like these fields should be just getters on the QueryList class (so setting their values would not be required) and that should also help with typings above.

packages/core/test/acceptance/query_spec.ts Show resolved Hide resolved
@AndrewKushnir
Copy link
Contributor

FYI, started global presubmit.

Copy link
Member

@JoostK JoostK left a comment

Choose a reason for hiding this comment

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

I'd personally mark this for minor and not patch, since it introduces new API.

@@ -135,28 +169,36 @@ export class QueryList<T> implements Iterable<T> {
* occurs.
*
* @param resultsTree The query results to store
* @returns `true` only if the new `resultTree` resulted in a different query set.
* @internal
Copy link
Member

Choose a reason for hiding this comment

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

Although this is marked @internal, it still shows up in the public api. As this is targeting patch I don't think we can/should hide this method in a patch release.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is an internal method which should never be called by anyone. I can take it out if you feel strongly about it.

Copy link
Member

Choose a reason for hiding this comment

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

So I'm fine with marking it internal, it's just that doing so could break projects who call this method manually. So we may need to go through a deprecation period.

packages/core/src/linker/query_list.ts Outdated Show resolved Hide resolved
@mhevery
Copy link
Contributor Author

mhevery commented Dec 15, 2020

presubmit

@AndrewKushnir
Copy link
Contributor

Global presubmit #2 (using Ivy).

@mhevery mhevery added the action: merge The PR is ready for merge by the caretaker label Jan 13, 2021
@mhevery
Copy link
Contributor Author

mhevery commented Jan 13, 2021

presubmit (deflake)

@mary-poppins
Copy link

You can preview af6d6cd at https://pr40091-af6d6cd.ngbuilds.io/.

Copy link
Contributor

@IgorMinar IgorMinar left a comment

Choose a reason for hiding this comment

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

LGTM

Reviewed-for: size-tracking, public-api

@mary-poppins
Copy link

You can preview 29c4f20 at https://pr40091-29c4f20.ngbuilds.io/.

@mary-poppins
Copy link

You can preview 469090e at https://pr40091-469090e.ngbuilds.io/.

@mary-poppins
Copy link

You can preview 4d81a44 at https://pr40091-4d81a44.ngbuilds.io/.

@mhevery mhevery added target: patch This PR is targeted for the next patch release and removed target: minor This PR is targeted for the next minor release labels Jan 14, 2021
… did not change.

Previous implementation would fire changes `QueryList.changes.subscribe`
whenever the `QueryList` was recomputed. This resulted in artificially
high number of change notifications, as it is possible that recomputing
`QueryList` results in the same list. When the `QueryList` gets recomputed
is an implementation detail and it should not be the thing which determines
how often change event should fire.

This change introduces a new `emitDistinctChangesOnly` option for
`ContentChildren` and `ViewChildren`.

```
export class QueryCompWithStrictChangeEmitParent {
  @ContentChildren('foo', {
    // This option will become the default in the future
    emitDistinctChangesOnly: true,
  })
  foos!: QueryList<any>;
}
```
Because the query now has `flags` which specify the mode, the static query
instruction can now be remove. It is simply normal query with `static` flag.
@mary-poppins
Copy link

You can preview 29cd0de at https://pr40091-29cd0de.ngbuilds.io/.

@AndrewKushnir AndrewKushnir added target: rc This PR is targeted for the next release-candidate and removed target: patch This PR is targeted for the next patch release labels Jan 14, 2021
AndrewKushnir pushed a commit that referenced this pull request Jan 14, 2021
… did not change. (#40091)

Previous implementation would fire changes `QueryList.changes.subscribe`
whenever the `QueryList` was recomputed. This resulted in artificially
high number of change notifications, as it is possible that recomputing
`QueryList` results in the same list. When the `QueryList` gets recomputed
is an implementation detail and it should not be the thing which determines
how often change event should fire.

This change introduces a new `emitDistinctChangesOnly` option for
`ContentChildren` and `ViewChildren`.

```
export class QueryCompWithStrictChangeEmitParent {
  @ContentChildren('foo', {
    // This option will become the default in the future
    emitDistinctChangesOnly: true,
  })
  foos!: QueryList<any>;
}
```

PR Close #40091
AndrewKushnir pushed a commit that referenced this pull request Jan 14, 2021
…#40091)

Because the query now has `flags` which specify the mode, the static query
instruction can now be remove. It is simply normal query with `static` flag.

PR Close #40091
AndrewKushnir pushed a commit that referenced this pull request Jan 14, 2021
…#40091)

Because the query now has `flags` which specify the mode, the static query
instruction can now be remove. It is simply normal query with `static` flag.

PR Close #40091
@AndrewKushnir
Copy link
Contributor

@mhevery this PR was merged into RC (11.1.x) and master branches, but there was a conflict of merging it to the patch branch (11.0.x). Could you please create a patch-only version of this change if needed? Thank you.

@JoostK
Copy link
Member

JoostK commented Jan 14, 2021

@mhevery this PR was merged into RC (11.1.x) and master branches, but there was a conflict of merging it to the patch branch (11.0.x). Could you please create a patch-only version of this change if needed? Thank you.

I believe RC should be sufficient; there's no real need for this to land in 11.0.x with 11.1 just around the corner (there might not even be another 11.0.x release?) and porting this is non-trivial as the linker code doesn't exist in 11.0.x.

@AndrewKushnir
Copy link
Contributor

Thanks @JoostK. Right, we also discussed that with @mhevery and agreed to keep this change in RC and master only (no changes to the patch branch).

@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 Feb 14, 2021
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 aio: preview area: core Issues related to the framework runtime cla: yes target: rc This PR is targeted for the next release-candidate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants