angular: docs: clarify ivy breaking change for `ngFor` and `
🐞 bug report
Affected Package
The issue seems to be related to package @angular/platform-browserIs this a regression?
Yes, the previous version in which this bug was not present was: v8.* (8.2.14)Description
Upgrading to Angular v9 broke the functionality of <select>
for us. The *ngFor
directive we use for populating it with <option>
elements now all of the sudden re-renders items if you simply refresh the collection with new instances, even if their contents are the same. This resets the selected option, which is not expected and not desirable. This wasn’t happening in v8. This isn’t happening in v9 either, unless BrowserAnimationsModule
is imported by the app-module
. This also can be worked around by using trackBy
, but for that you’d need to track down all the problematic ngFor
directives after upgrading to v9.
🔬 Minimal Reproduction
Click here for v9 repro repository Click here to see the same v9 repro app in action Same demo working without problems in Angular v8 - although StackBlitz doesn’t have this problem with v9 either (not sure why), but at least helps illustrate how it was working before
The v9 repro is based on ng new
of Angular CLI 9.0.5. If building yourself, run locally with ng serve
, because if imported into StackBlitz
, the issue can’t be reproduced.
- Choose an item in any drop down (the two are linked and simply illustrate the
trackBy
difference) - Notice that the first button adds a new item but the selected option doesn’t get reset.
- Clicking the second button though resets the selected option in the first drop down.
🔥 Exception or Error
None
🌍 Your Environment
Angular Version:
Angular CLI: 9.0.5
Node: 10.16.3
OS: win32 x64
Angular: 9.0.5
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.900.5
@angular-devkit/build-angular 0.900.5
@angular-devkit/build-optimizer 0.900.5
@angular-devkit/build-webpack 0.900.5
@angular-devkit/core 9.0.5
@angular-devkit/schematics 9.0.5
@ngtools/webpack 9.0.5
@schematics/angular 9.0.5
@schematics/update 0.900.5
rxjs 6.5.4
typescript 3.7.5
webpack 4.41.2
Anything else relevant?
Happens only in Angular v9, when BrowserAnimationsModule
is imported into AppModule
and ngFor
is not set up with trackBy
.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 3
- Comments: 17 (8 by maintainers)
Commits related to this issue
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
- fix(forms): ensure that selected option element matches the ngModel value Given the following component: @Component({ selector: 'ng-model-select-form', template: ` <select [(... — committed to theodorejb/angular by theodorejb 4 years ago
I’m re-opening this issue while we discuss the best way to tackle this. This change was expected based on how Ivy works (so
trackBy
seems like a good fix), but I agree the experience is confusing and there’s probably a way to make it less painful for users.This is still happening on Angular 9.1.3. Without using
trackBy
, when assigning a new list to the*ngFor
list in a select, it sets the select back to the default value ‘’, but the form contains the correct id and the formControlName is not working with select correctly.Also figured out
BrowserAnimationsModule
is the cause of this when adding to AppModuleThis appears to be related to the breaking change related to insertion order (reference):
In the stackblitz example, you can see that when Ivy is disabled, the new items are added to the front of the list (add a break on: subtree modifications to the dom). If you go to the settings in stackblitz and check the box to “Enable Ivy”, you’ll see that new items are added to the end of the list. To clarify, both Ivy and View Engine are re-rendering the items because you don’t have
trackBy
. The difference is the location of the newly inserted items. BecauseBrowserAnimationsModule
queues up the removal of the old items in order to animate them, you will briefly have duplicate items in the list if you don’t usetrackBy
. In the Ivy example, since the old items being removed are above the new items, when they’re removed, the browser sees this as the selected item being removed. As a quick fix, I would recommend usingtrackBy
to avoid this issue.I could not tell you if this is really a bug or a feature of the v9 nevertheless the
trackBy
feature is dedicated to avoid re-rendering the DOM to improve the performances. It seems to me that you should have used atrackBy
and your issue is in fact, a bad design from your project.Even if Angular considers it as a bug, you should really use the
trackBy
anytime it is possible.It looks like there’s attention and progress on this bug. If a fix is found, is there any chance it could be introduced with a minor or patch update in the near future, since it’s clearly a significant regression bug? Or would it more likely have to wait until the Angular 10 release?
I’m just trying to gauge the likelihood that it will be fixed within our development window, or whether I should be looking to implement trackBy in all the places where we have issues.
@matsko and @atscott - I wonder if it could be possible to identify such cases and provide a migration to help?