-
Notifications
You must be signed in to change notification settings - Fork 25.2k
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(forms): ensure select[multiple] retains selections #12654
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! Looks fine, but requesting minor changes.
` | ||
}) | ||
class NgModelSelectMultipleForm { | ||
@Input() selectedCities: any[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The @input decorator is unnecessary - can you remove?
comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}]; | ||
comp.selectedCities = []; | ||
detectChangesAndTick(); | ||
const select = fixture.debugElement.queryAll(By.css('select'))[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
queryAll
doesn't seem necessary here, given that there is only one select
element in the test component. Can you switch to query
? Then you won't have to look for [0]
.
const select = fixture.debugElement.queryAll(By.css('select'))[0]; | ||
select.nativeElement.value = '1: Object'; | ||
dispatchEvent(select.nativeElement, 'change'); | ||
detectChangesAndTick(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After this change detection run, can you check that the options are selected as you expect? Will make the intent of the test clearer.
detectChangesAndTick(); | ||
assertOptionElementSelectedState([false, true, false, false]); | ||
})); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for the 'pop' case?
const comp = fixture.componentInstance; | ||
comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}]; | ||
comp.selectedCities = []; | ||
detectChangesAndTick(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is a bit harder to read because there are no line breaks. Can you add a line break after each logical section? So perhaps here, after the selection is made, and after the new value is pushed?
@gary-b do you have plan to update this PR ? |
Yes, ill work on this and #12519 tomorrow |
If you bound an array to select[multiple] via ngModel and subsequently changed the options to select from, the UI would drop any selections made since by the user. This was due to SelectMultipleControlValueAccessor not keeping a reference to the new model arrays it generated when users interacted with the select control. Update code to keep the reference. Closes angular#12527
c638a5f
to
af4328f
Compare
let fixture: ComponentFixture<NgModelSelectMultipleForm>; | ||
let comp: NgModelSelectMultipleForm; | ||
|
||
beforeEach(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used by other tests in #12519
}; | ||
|
||
const setSelectedCities = (selectedCities: any): void => { | ||
comp.selectedCities = selectedCities; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used by other tests in #12519
As before, I may need to rebase this again after #12519 is merged as the tests for both introduce the same describe(). They also use the same helper functions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
If you bound an array to select[multiple] via ngModel and subsequently changed the options to select from, the UI would drop any selections made since by the user. This was due to SelectMultipleControlValueAccessor not keeping a reference to the new model arrays it generated when users interacted with the select control. Update code to keep the reference. Closes #12527 Closes #12654
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. |
What kind of change does this PR introduce?
What is the current behavior?
Closes #12527. The original repro had quite a lot going on, here is a more distilled version Select a few options, then push or pop from the options list.
If you bound an array to select[multiple] via ngModel and subsequently changed the options to select from, the UI would drop any selections that the user had made since.
After options change SelectMultipleControlValueAccessor will read the model again and set the state of the options. The problem was that it didn't keep a reference to the latest model.
What is the new behavior?
SelectMultipleControlValueAccessor maintains an up to date reference to the model. You can now add and remove items from the options and currently selected items will remain selected. Items in the model array that didn't have matching options in the select list will also cause their corresponding options to become selected once added.
Does this PR introduce a breaking change? (check one with "x")
Whoever looks at this could look at #12519 again too. Ill need to do a little bit of refactoring to the tests when the last one is committed, if they both are accepted that is :P