Skip to content

Commit 916c532

Browse files
crisbetommalerba
authored andcommitted
fix(checkbox): not marked as touched immediately on blur with OnPush change detection (#15001)
Fixes a checkbox which is blurred inside a component with `OnPush` change detection not being marked as touched. Fixes #14980.
1 parent 6a07d0d commit 916c532

File tree

2 files changed

+37
-3
lines changed

2 files changed

+37
-3
lines changed

src/lib/checkbox/checkbox.spec.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
flushMicrotasks,
77
} from '@angular/core/testing';
88
import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms';
9-
import {Component, DebugElement, ViewChild, Type} from '@angular/core';
9+
import {Component, DebugElement, ViewChild, Type, ChangeDetectionStrategy} from '@angular/core';
1010
import {By} from '@angular/platform-browser';
1111
import {dispatchFakeEvent} from '@angular/cdk/testing';
1212
import {MatCheckbox, MatCheckboxChange, MatCheckboxModule} from './index';
@@ -689,7 +689,7 @@ describe('MatCheckbox', () => {
689689
}));
690690
});
691691

692-
describe('aria-label ', () => {
692+
describe('aria-label', () => {
693693
let checkboxDebugElement: DebugElement;
694694
let checkboxNativeElement: HTMLElement;
695695
let inputElement: HTMLInputElement;
@@ -916,6 +916,31 @@ describe('MatCheckbox', () => {
916916
expect(ngModel.touched).toBe(true);
917917
}));
918918

919+
it('should mark the element as touched on blur when inside an OnPush parent', fakeAsync(() => {
920+
fixture.destroy();
921+
TestBed.resetTestingModule();
922+
fixture = createComponent(CheckboxWithNgModelAndOnPush);
923+
checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox));
924+
checkboxNativeElement = checkboxDebugElement.nativeElement;
925+
checkboxInstance = checkboxDebugElement.componentInstance;
926+
inputElement = <HTMLInputElement>checkboxNativeElement.querySelector('input');
927+
ngModel = checkboxDebugElement.injector.get<NgModel>(NgModel);
928+
929+
inputElement.click();
930+
fixture.detectChanges();
931+
flush();
932+
933+
expect(checkboxNativeElement.classList).not.toContain('ng-touched');
934+
935+
dispatchFakeEvent(inputElement, 'blur');
936+
fixture.detectChanges();
937+
flushMicrotasks();
938+
fixture.detectChanges();
939+
940+
expect(checkboxNativeElement.classList).toContain('ng-touched');
941+
}));
942+
943+
919944
it('should not throw an error when disabling while focused', fakeAsync(() => {
920945
expect(() => {
921946
// Focus the input element because after disabling, the `blur` event should automatically
@@ -1176,6 +1201,12 @@ class CheckboxWithNgModel {
11761201
isRequired: boolean = true;
11771202
}
11781203

1204+
@Component({
1205+
template: `<mat-checkbox [required]="isRequired" [(ngModel)]="isGood">Be good</mat-checkbox>`,
1206+
changeDetection: ChangeDetectionStrategy.OnPush,
1207+
})
1208+
class CheckboxWithNgModelAndOnPush extends CheckboxWithNgModel {}
1209+
11791210
/** Simple test component with multiple checkboxes. */
11801211
@Component(({
11811212
template: `

src/lib/checkbox/checkbox.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,10 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAc
212212
// (such as a form control's 'ng-touched') will cause a changed-after-checked error.
213213
// See https://github.com/angular/angular/issues/17793. To work around this, we defer
214214
// telling the form control it has been touched until the next tick.
215-
Promise.resolve().then(() => this._onTouched());
215+
Promise.resolve().then(() => {
216+
this._onTouched();
217+
_changeDetectorRef.markForCheck();
218+
});
216219
}
217220
});
218221
}

0 commit comments

Comments
 (0)