diff --git a/goldens/material/chips/index.api.md b/goldens/material/chips/index.api.md index a46a68404f9f..8e7323e3f684 100644 --- a/goldens/material/chips/index.api.md +++ b/goldens/material/chips/index.api.md @@ -165,6 +165,7 @@ export class MatChipGrid extends MatChipSet implements AfterContentInit, AfterVi protected _allowFocusEscape(): void; _blur(): void; readonly change: EventEmitter; + _change(): void; get chipBlurChanges(): Observable; protected _chipInput: MatChipTextControl; // (undocumented) diff --git a/src/material/chips/chip-grid.spec.ts b/src/material/chips/chip-grid.spec.ts index 7b9357f6b2b5..e0ea3b3ac56a 100644 --- a/src/material/chips/chip-grid.spec.ts +++ b/src/material/chips/chip-grid.spec.ts @@ -1015,6 +1015,44 @@ describe('MatChipGrid', () => { })); }); + describe('chip with form control', () => { + let fixture: ComponentFixture; + let component: ChipsFormControlUpdate; + let nativeInput: HTMLInputElement; + let nativeButton: HTMLButtonElement; + + beforeEach(() => { + fixture = createComponent(ChipsFormControlUpdate); + component = fixture.componentInstance; + nativeInput = fixture.nativeElement.querySelector('input'); + nativeButton = fixture.nativeElement.querySelector('button[id="save"]'); + }); + + it('should update the form control value when pressed enter', fakeAsync(() => { + nativeInput.value = 'hello'; + nativeInput.focus(); + fixture.detectChanges(); + + dispatchKeyboardEvent(document.activeElement!, 'keydown', ENTER); + fixture.detectChanges(); + flush(); + + expect(component.keywordChipControl.value).not.toBeNull(); + expect(component.keywordChipControl.value.length).toBe(1); + expect(nativeButton.disabled).toBeFalsy(); + + nativeInput.value = 'how are you ?'; + nativeInput.focus(); + fixture.detectChanges(); + + dispatchKeyboardEvent(document.activeElement!, 'keydown', ENTER); + fixture.detectChanges(); + flush(); + + expect(component.keywordChipControl.value.length).toBe(2); + })); + }); + function createComponent( component: Type, direction: Direction = 'ltr', @@ -1220,3 +1258,37 @@ class ChipGridWithRemove { this.chips.splice(event.chip.value, 1); } } + +@Component({ + template: ` + + Keywords + + @for (keyword of keywords; track keyword) { + {{keyword}} + } + + + + + `, + standalone: false, +}) +class ChipsFormControlUpdate { + keywords = new Array(); + keywordChipControl = new FormControl(); + + constructor() { + this.keywordChipControl.setValidators(Validators.required); + } + + add(event: MatChipInputEvent): void { + const value = (event.value || '').trim(); + + if (value) { + this.keywords.push(value); + } + + event.chipInput.clear(); + } +} diff --git a/src/material/chips/chip-grid.ts b/src/material/chips/chip-grid.ts index 9741ca8453e9..8943691879cc 100644 --- a/src/material/chips/chip-grid.ts +++ b/src/material/chips/chip-grid.ts @@ -415,6 +415,16 @@ export class MatChipGrid } } + /** When called, propagates the changes and update the immediately */ + _change() { + if (!this.disabled) { + // Timeout is needed to wait for the focus() event trigger on chip input. + setTimeout(() => { + this._propagateChanges(); + }); + } + } + /** * Removes the `tabindex` from the chip grid and resets it back afterwards, allowing the * user to tab out of it. This prevents the grid from capturing focus and redirecting diff --git a/src/material/chips/chip-input.ts b/src/material/chips/chip-input.ts index e02a5ea5b98e..98cda6265f3b 100644 --- a/src/material/chips/chip-input.ts +++ b/src/material/chips/chip-input.ts @@ -198,7 +198,8 @@ export class MatChipInput implements MatChipTextControl, OnChanges, OnDestroy { value: this.inputElement.value, chipInput: this, }); - + this._chipGrid._change(); + this._chipGrid.stateChanges.next(); event?.preventDefault(); } }