-
Notifications
You must be signed in to change notification settings - Fork 24.8k
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
docs: clarify ivy breaking change for ngFor
and <select>
with animations and no trackBy
#35896
Comments
I could not tell you if this is really a bug or a feature of the v9 nevertheless the Even if Angular considers it as a bug, you should really use the |
This is still happening on Angular 9.1.3. Without using Also figured out |
This 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 |
@rsheptolut unfortunately the only way forward with this is to use |
How is this a valid solution to suggest using I have 15 applications using Angular 9 that all inherited this issue and have no way to fix this without tremendous overhaul of all applications by adding Angular team should not expect people who rely on Angular to rework their entire source code and have temporary work arounds when they break huge components of html! |
RTFM. |
…alue Given the following component: @component({ selector: 'ng-model-select-form', template: ` <select [(ngModel)]="selectedCity"> <option *ngFor="let c of cities" [ngValue]="c">{{c.name}}</option> </select> ` }) class NgModelSelectForm { selectedCity: {name: string} | null = null; cities: {name: string}[] = []; } Previously, when values were pushed into the cities array after a delay (e.g. from an API request), IE 11 and Safari (tested in v11-v13) would display the first city in the dropdown as being selected, despite the `selectedCity` property still being null. This broke the form for end users, because the UI state didn't match the component state. In some cases this could completely prevent users from submitting a form. The problem occurs because Angular sets the select element value before appending an option's child text node to the DOM. Once the text node is appended, Safari and IE 11 ignore the previously set select value and instead mark the first option element as selected. This commit fixes the issue by setting the select value again in the `AfterViewChecked` hook. `NgZone.onStable` is used to wait for changes to be applied when using `BrowserAnimationsModule`. This results in correct behavior across all supported browsers (an option is only selected when its value matches that of the select element). This also fixes an issue in all browsers caused by delayed element removal when using the animations module, where select dropdowns would display an option that didn't match the ngModel after removing the selected option. This occurred because when a selected option is removed, Angular changes the select element value before actually removing the option from the DOM. Then when the option is finally removed from the DOM, the browser would change the select value to that of the first option, even though it didn't match the ngModel. Fixes angular#14505, fixes angular#18430, fixes angular#35896.
…alue Given the following component: @component({ selector: 'ng-model-select-form', template: ` <select [(ngModel)]="selectedCity"> <option *ngFor="let c of cities" [ngValue]="c">{{c.name}}</option> </select> ` }) class NgModelSelectForm { selectedCity: {name: string} | null = null; cities: {name: string}[] = []; } Previously, when values were pushed into the cities array after a delay (e.g. from an API request), IE 11 and Safari (tested in v11-v13) would display the first city in the dropdown as being selected, despite the `selectedCity` property still being null. This broke the form for end users, because the UI state didn't match the component state. In some cases this could completely prevent users from submitting a form. The problem occurs because Angular sets the select element value before appending an option's child text node to the DOM. Once the text node is appended, Safari and IE 11 ignore the previously set select value and instead mark the first option element as selected. This commit fixes the issue by setting the select value again in the `AfterViewChecked` hook. `NgZone.onStable` is used to wait for changes to be applied when using `BrowserAnimationsModule`. This results in correct behavior across all supported browsers (an option is only selected when its value matches that of the select element). This also fixes an issue in all browsers caused by delayed element removal when using the animations module, where select dropdowns would display an option that didn't match the ngModel after removing the selected option. This occurred because when a selected option is removed, Angular changes the select element value before actually removing the option from the DOM. Then when the option is finally removed from the DOM, the browser would change the select value to that of the first option, even though it didn't match the ngModel. Fixes angular#14505, fixes angular#18430, fixes angular#35896.
…value Given the following component: @component({ selector: 'ng-model-select-form', template: ` <select [(ngModel)]="selectedCity"> <option *ngFor="let c of cities" [ngValue]="c">{{c.name}}</option> </select> ` }) class NgModelSelectForm { selectedCity: {name: string} | null = null; cities: {name: string}[] = []; } Previously, when values were pushed into the cities array after a delay (e.g. from an API request), IE 11 and Safari (tested in v11-v13) would display the first city in the dropdown as being selected, despite the `selectedCity` property still being null. This broke the form for end users, because the UI state didn't match the component state. In some cases this could completely prevent users from submitting a form. The problem occurs because Angular sets the select element value before appending an option's child text node to the DOM. Once the text node is appended, Safari and IE 11 ignore the previously set select value and instead mark the first option element as selected. This commit fixes the issue by setting the select value again in the `AfterViewChecked` hook. `NgZone.onStable` is used to wait for changes to be applied when using `BrowserAnimationsModule`. This results in correct behavior across all supported browsers (an option is only selected when its value matches that of the select element). This also fixes an issue in all browsers caused by delayed element removal when using the animations module, where select dropdowns would display an option that didn't match the ngModel after removing the selected option. This occurred because when a selected option is removed, Angular changes the select element value before actually removing the option from the DOM. Then when the option is finally removed from the DOM, the browser would change the select value to that of the first option, even though it didn't match the ngModel. Fixes angular#14505, fixes angular#18430, fixes angular#35896.
…value Given the following component: @component({ selector: 'ng-model-select-form', template: ` <select [(ngModel)]="selectedCity"> <option *ngFor="let c of cities" [ngValue]="c">{{c.name}}</option> </select> ` }) class NgModelSelectForm { selectedCity: {name: string} | null = null; cities: {name: string}[] = []; } Previously, when values were pushed into the cities array after a delay (e.g. from an API request), IE 11 and Safari (tested in v11-v13) would display the first city in the dropdown as being selected, despite the `selectedCity` property still being null. This broke the form for end users, because the UI state didn't match the component state. In some cases this could completely prevent users from submitting a form. The problem occurs because Angular sets the select element value before appending an option's child text node to the DOM. Once the text node is appended, Safari and IE 11 ignore the previously set select value and instead mark the first option element as selected. This commit fixes the issue by setting the select value again in the `AfterViewChecked` hook. `NgZone.onStable` is used to wait for changes to be applied when using `BrowserAnimationsModule`. This results in correct behavior across all supported browsers (an option is only selected when its value matches that of the select element). This also fixes an issue in all browsers caused by delayed element removal when using the animations module, where select dropdowns would display an option that didn't match the ngModel after removing the selected option. This occurred because when a selected option is removed, Angular changes the select element value before actually removing the option from the DOM. Then when the option is finally removed from the DOM, the browser would change the select value to that of the first option, even though it didn't match the ngModel. Fixes angular#14505, fixes angular#18430, fixes angular#35896.
It looks like this is a bug that requires a code fix, so removing the |
ngFor
and <select>
with animations and no trackBy
Having discussed offline with @atscott - I now understand that this is part of how Angular works (by design) and it just happens that the browser didn't notice with View Engine, while it becomes obvious under Ivy. Therefore this specific case needs to be listed more clearly in our Ivy breaking changes so that people can more quickly resolve their problem. |
An internal change in Ivy has surfaced issues in previosly broken code. This change adds a note to the Ivy compatibility guide as well as the TrackByFunction api docs. Fixes angular#35896
An internal change in Ivy has surfaced issues in previosly broken code. This change adds a note to the Ivy compatibility guide as well as the TrackByFunction api docs. Fixes angular#35896
…ide (angular#42338) An internal change in Ivy has surfaced issues in previosly broken code. This change adds a note to the Ivy compatibility guide as well as the TrackByFunction api docs. Fixes angular#35896 PR Close angular#42338
…ide (angular#42338) An internal change in Ivy has surfaced issues in previosly broken code. This change adds a note to the Ivy compatibility guide as well as the TrackByFunction api docs. Fixes angular#35896 PR Close angular#42338
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
🐞 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, unlessBrowserAnimationsModule
is imported by theapp-module
. This also can be worked around by usingtrackBy
, but for that you'd need to track down all the problematicngFor
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 withng serve
, because if imported intoStackBlitz
, the issue can't be reproduced.trackBy
difference)🔥 Exception or Error
🌍 Your Environment
Angular Version:
Anything else relevant?
Happens only in Angular v9, when
BrowserAnimationsModule
is imported intoAppModule
andngFor
is not set up withtrackBy
.The text was updated successfully, but these errors were encountered: