Skip to content

Commit 5f4148f

Browse files
authored
fix(material/select): value set through property not being propagated to value accessor (#10246)
Fixes values set through the `value` property not being propagated to the `value` in the `ControlValueAccessor`. Fixes #10214.
1 parent fbd7361 commit 5f4148f

File tree

3 files changed

+43
-10
lines changed

3 files changed

+43
-10
lines changed

src/material-experimental/mdc-select/select.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,6 +2155,17 @@ describe('MDC-based MatSelect', () => {
21552155
.withContext(`Expected label to have an asterisk, as control was required.`)
21562156
.toBeTruthy();
21572157
}));
2158+
2159+
it('should propagate the value set through the `value` property to the form field', fakeAsync(() => {
2160+
const control = fixture.componentInstance.control;
2161+
2162+
expect(control.value).toBeFalsy();
2163+
2164+
fixture.componentInstance.select.value = 'pizza-1';
2165+
fixture.detectChanges();
2166+
2167+
expect(control.value).toBe('pizza-1');
2168+
}));
21582169
});
21592170

21602171
describe('disabled behavior', () => {

src/material/select/select.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,6 +2170,17 @@ describe('MatSelect', () => {
21702170
.not.withContext(`Expected label to have an asterisk, as control was required.`)
21712171
.toBeNull();
21722172
}));
2173+
2174+
it('should propagate the value set through the `value` property to the form field', fakeAsync(() => {
2175+
const control = fixture.componentInstance.control;
2176+
2177+
expect(control.value).toBeFalsy();
2178+
2179+
fixture.componentInstance.select.value = 'pizza-1';
2180+
fixture.detectChanges();
2181+
2182+
expect(control.value).toBe('pizza-1');
2183+
}));
21732184
});
21742185

21752186
describe('disabled behavior', () => {

src/material/select/select.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -423,13 +423,10 @@ export abstract class _MatSelectBase<C>
423423
return this._value;
424424
}
425425
set value(newValue: any) {
426-
// Always re-assign an array, because it might have been mutated.
427-
if (newValue !== this._value || (this._multiple && Array.isArray(newValue))) {
428-
if (this.options) {
429-
this._setSelectionByValue(newValue);
430-
}
426+
const hasAssigned = this._assignValue(newValue);
431427

432-
this._value = newValue;
428+
if (hasAssigned) {
429+
this._onChange(newValue);
433430
}
434431
}
435432
private _value: any;
@@ -661,7 +658,7 @@ export abstract class _MatSelectBase<C>
661658
* @param value New value to be written to the model.
662659
*/
663660
writeValue(value: any): void {
664-
this.value = value;
661+
this._assignValue(value);
665662
}
666663

667664
/**
@@ -886,10 +883,10 @@ export abstract class _MatSelectBase<C>
886883
throw getMatSelectNonArrayValueError();
887884
}
888885

889-
value.forEach((currentValue: any) => this._selectValue(currentValue));
886+
value.forEach((currentValue: any) => this._selectOptionByValue(currentValue));
890887
this._sortValues();
891888
} else {
892-
const correspondingOption = this._selectValue(value);
889+
const correspondingOption = this._selectOptionByValue(value);
893890

894891
// Shift focus to the active item. Note that we shouldn't do this in multiple
895892
// mode, because we don't know what option the user interacted with last.
@@ -909,7 +906,7 @@ export abstract class _MatSelectBase<C>
909906
* Finds and selects and option based on its value.
910907
* @returns Option that has the corresponding value.
911908
*/
912-
private _selectValue(value: any): MatOption | undefined {
909+
private _selectOptionByValue(value: any): MatOption | undefined {
913910
const correspondingOption = this.options.find((option: MatOption) => {
914911
// Skip options that are already in the model. This allows us to handle cases
915912
// where the same primitive value is selected multiple times.
@@ -936,6 +933,20 @@ export abstract class _MatSelectBase<C>
936933
return correspondingOption;
937934
}
938935

936+
/** Assigns a specific value to the select. Returns whether the value has changed. */
937+
private _assignValue(newValue: any | any[]): boolean {
938+
// Always re-assign an array, because it might have been mutated.
939+
if (newValue !== this._value || (this._multiple && Array.isArray(newValue))) {
940+
if (this.options) {
941+
this._setSelectionByValue(newValue);
942+
}
943+
944+
this._value = newValue;
945+
return true;
946+
}
947+
return false;
948+
}
949+
939950
/** Sets up a key manager to listen to keyboard events on the overlay panel. */
940951
private _initKeyManager() {
941952
this._keyManager = new ActiveDescendantKeyManager<MatOption>(this.options)

0 commit comments

Comments
 (0)