Skip to content

Commit e6b842c

Browse files
authored
fix(material/chips): initial value from forms not reflected in the view (#26052)
Fixes that the chip listbox wasn't reflecting its initial value in the view, because `writeValue` gets called initially before the chips have been initialized. Fixes #26041.
1 parent 57676e4 commit e6b842c

File tree

2 files changed

+40
-16
lines changed

2 files changed

+40
-16
lines changed

src/material/chips/chip-listbox.spec.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -737,30 +737,35 @@ describe('MDC-based MatChipListbox', () => {
737737
});
738738

739739
describe('multiple selection', () => {
740-
beforeEach(() => {
741-
fixture = createComponent(MultiSelectionChipListbox);
742-
chips = fixture.componentInstance.chips;
743-
});
744-
745-
it('should take an initial view value with reactive forms', () => {
746-
fixture.componentInstance.control = new FormControl(['pizza-1']);
740+
it('should take an initial view value with reactive forms', fakeAsync(() => {
741+
fixture = createComponent(MultiSelectionChipListbox, undefined, initFixture => {
742+
initFixture.componentInstance.control = new FormControl(['pizza-1', 'pasta-6']);
743+
initFixture.componentInstance.selectable = true;
744+
});
747745
fixture.detectChanges();
746+
flush();
748747

749-
const array = chips.toArray();
748+
const array = fixture.componentInstance.chips.toArray();
750749

751-
expect(array[1].selected).withContext('Expect pizza-1 chip to be selected').toBeTruthy();
750+
expect(array[1].selected).withContext('Expect pizza-1 chip to be selected').toBe(true);
751+
expect(array[6].selected).withContext('Expect pasta-6 chip to be selected').toBe(true);
752752

753753
dispatchKeyboardEvent(primaryActions[1], 'keydown', SPACE);
754754
fixture.detectChanges();
755+
flush();
755756

756757
expect(array[1].selected)
757-
.withContext('Expect chip to be not selected after toggle selected')
758-
.toBeFalsy();
759-
});
758+
.withContext('Expect pizza-1 chip to no longer be selected')
759+
.toBe(false);
760+
expect(array[6].selected)
761+
.withContext('Expect pasta-6 chip to remain selected')
762+
.toBe(true);
763+
}));
760764

761765
it('should set the view value from the form', () => {
766+
fixture = createComponent(MultiSelectionChipListbox);
762767
const chipListbox = fixture.componentInstance.chipListbox;
763-
const array = chips.toArray();
768+
const array = fixture.componentInstance.chips.toArray();
764769

765770
expect(chipListbox.value)
766771
.withContext('Expect chip listbox to have no initial value')
@@ -773,6 +778,8 @@ describe('MDC-based MatChipListbox', () => {
773778
});
774779

775780
it('should update the form value when the view changes', () => {
781+
fixture = createComponent(MultiSelectionChipListbox);
782+
776783
expect(fixture.componentInstance.control.value)
777784
.withContext(`Expected the control's value to be empty initially.`)
778785
.toEqual(null);
@@ -786,8 +793,10 @@ describe('MDC-based MatChipListbox', () => {
786793
});
787794

788795
it('should clear the selection when a nonexistent option value is selected', () => {
789-
const array = chips.toArray();
796+
fixture = createComponent(MultiSelectionChipListbox);
797+
chips = fixture.componentInstance.chips;
790798

799+
const array = fixture.componentInstance.chips.toArray();
791800
fixture.componentInstance.control.setValue(['pizza-1']);
792801
fixture.detectChanges();
793802

@@ -805,7 +814,8 @@ describe('MDC-based MatChipListbox', () => {
805814
});
806815

807816
it('should clear the selection when the control is reset', () => {
808-
const array = chips.toArray();
817+
fixture = createComponent(MultiSelectionChipListbox);
818+
const array = fixture.componentInstance.chips.toArray();
809819

810820
fixture.componentInstance.control.setValue(['pizza-1']);
811821
fixture.detectChanges();
@@ -824,6 +834,7 @@ describe('MDC-based MatChipListbox', () => {
824834
function createComponent<T>(
825835
component: Type<T>,
826836
direction: Direction = 'ltr',
837+
beforeInitialChangeDetection?: (fixture: ComponentFixture<T>) => void,
827838
): ComponentFixture<T> {
828839
directionality = {
829840
value: direction,
@@ -840,6 +851,7 @@ describe('MDC-based MatChipListbox', () => {
840851
}).compileComponents();
841852

842853
fixture = TestBed.createComponent<T>(component);
854+
beforeInitialChangeDetection?.(fixture);
843855
fixture.detectChanges();
844856

845857
chipListboxDebugElement = fixture.debugElement.query(By.directive(MatChipListbox))!;
@@ -926,7 +938,7 @@ class MultiSelectionChipListbox {
926938
{value: 'pasta-6', viewValue: 'Pasta'},
927939
{value: 'sushi-7', viewValue: 'Sushi'},
928940
];
929-
control = new FormControl<string | null>(null);
941+
control = new FormControl<string[] | null>(null);
930942
isRequired: boolean;
931943
tabIndexOverride: number;
932944
selectable: boolean;

src/material/chips/chip-listbox.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ export class MatChipListbox
102102
// TODO: MDC uses `grid` here
103103
protected override _defaultRole = 'listbox';
104104

105+
/** Value that was assigned before the listbox was initialized. */
106+
private _pendingInitialValue: any;
107+
105108
/** Whether the user should be allowed to select multiple chips. */
106109
@Input()
107110
get multiple(): boolean {
@@ -188,6 +191,13 @@ export class MatChipListbox
188191
override _chips: QueryList<MatChipOption>;
189192

190193
ngAfterContentInit() {
194+
if (this._pendingInitialValue !== undefined) {
195+
Promise.resolve().then(() => {
196+
this._setSelectionByValue(this._pendingInitialValue, false);
197+
this._pendingInitialValue = undefined;
198+
});
199+
}
200+
191201
this._chips.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {
192202
// Update listbox selectable/multiple properties on chips
193203
this._syncListboxProperties();
@@ -236,6 +246,8 @@ export class MatChipListbox
236246
writeValue(value: any): void {
237247
if (this._chips) {
238248
this._setSelectionByValue(value, false);
249+
} else if (value != null) {
250+
this._pendingInitialValue = value;
239251
}
240252
}
241253

0 commit comments

Comments
 (0)