Skip to content

Commit 0783279

Browse files
committed
fix(material/chips): update form control immediately when chips is removed
Currently, when we removed chips, the value is updated in form control only when we blur. This fix will update the value of form control immediately when removed Fixes #30566
1 parent d02338b commit 0783279

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed

goldens/material/chips/index.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ export class MatChipGrid extends MatChipSet implements AfterContentInit, AfterVi
189189
protected _allowFocusEscape(): void;
190190
_blur(): void;
191191
readonly change: EventEmitter<MatChipGridChange>;
192+
_change(): void;
192193
get chipBlurChanges(): Observable<MatChipEvent>;
193194
protected _chipInput?: MatChipTextControl;
194195
// (undocumented)

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,27 @@ describe('MatChipGrid', () => {
10891089
}));
10901090
});
10911091

1092+
it('should update the form control immediately when remove button is clicked', fakeAsync(() => {
1093+
const fixture = createComponent(ChipGridWithRemoveAndFormControl, undefined, []);
1094+
1095+
const component = fixture.componentRef.instance;
1096+
1097+
flush();
1098+
const trailingActions = chipGridNativeElement.querySelectorAll<HTMLElement>(
1099+
'.mdc-evolution-chip__action--secondary',
1100+
);
1101+
const chip = chips.get(2)!;
1102+
chip.focus();
1103+
fixture.detectChanges();
1104+
1105+
trailingActions[2].click();
1106+
fixture.detectChanges();
1107+
flush();
1108+
1109+
expect(component.formControl.value?.length).toBe(3);
1110+
expect(component.formControl.value?.indexOf('tutorial')).toBe(-1);
1111+
}));
1112+
10921113
function createComponent<T>(
10931114
component: Type<T>,
10941115
direction: Direction = 'ltr',
@@ -1315,3 +1336,50 @@ class ChipGridWithoutInput {
13151336
chips = ['Pizza', 'Pasta', 'Tacos'];
13161337
placeholder: string;
13171338
}
1339+
1340+
@Component({
1341+
template: `
1342+
<mat-form-field>
1343+
<mat-chip-grid #chipGrid [formControl]="formControl">
1344+
@for (keyword of keywords(); track keyword) {
1345+
<mat-chip-row (removed)="removeKeyword(keyword)">
1346+
{{keyword}}
1347+
<span matChipRemove>Remove</span>
1348+
</mat-chip-row>
1349+
}
1350+
</mat-chip-grid>
1351+
<input
1352+
placeholder="New keyword..."
1353+
[matChipInputFor]="chipGrid"
1354+
/>
1355+
</mat-form-field>
1356+
`,
1357+
imports: [
1358+
MatChipGrid,
1359+
MatChipRow,
1360+
MatChipInput,
1361+
MatFormField,
1362+
MatChipRemove,
1363+
ReactiveFormsModule,
1364+
],
1365+
})
1366+
class ChipGridWithRemoveAndFormControl {
1367+
readonly keywords = signal(['angular', 'how-to', 'tutorial', 'accessibility']);
1368+
readonly formControl = new FormControl([...this.keywords()]);
1369+
1370+
constructor() {
1371+
this.formControl.setValidators(Validators.required);
1372+
}
1373+
1374+
removeKeyword(keyword: string) {
1375+
this.keywords.update(keywords => {
1376+
const index = keywords.indexOf(keyword);
1377+
if (index < 0) {
1378+
return keywords;
1379+
}
1380+
1381+
keywords.splice(index, 1);
1382+
return [...keywords];
1383+
});
1384+
}
1385+
}

src/material/chips/chip-grid.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ export class MatChipGrid
282282
this.stateChanges.next();
283283
});
284284

285+
this.chipRemovedChanges.pipe(takeUntil(this._destroyed)).subscribe(() => {
286+
this._change();
287+
this.stateChanges.next();
288+
});
289+
285290
merge(this.chipFocusChanges, this._chips.changes)
286291
.pipe(takeUntil(this._destroyed))
287292
.subscribe(() => this.stateChanges.next());
@@ -435,6 +440,16 @@ export class MatChipGrid
435440
}
436441
}
437442

443+
/** When called, propagates the changes and update the immediately */
444+
_change() {
445+
if (!this.disabled) {
446+
// Timeout is needed to wait for the focus() event trigger on chip input.
447+
setTimeout(() => {
448+
this._propagateChanges();
449+
});
450+
}
451+
}
452+
438453
/**
439454
* Removes the `tabindex` from the chip grid and resets it back afterwards, allowing the
440455
* user to tab out of it. This prevents the grid from capturing focus and redirecting

0 commit comments

Comments
 (0)