fix(forms): don't override form group's dirty state when disabling controls (#24591)
Update packages/forms/src/model.ts Co-Authored-By: martinsik <martin.sikora.ahoj@gmail.com> PR Close #24591
This commit is contained in:
parent
2da82db3bc
commit
ef6728207b
|
@ -495,6 +495,10 @@ export abstract class AbstractControl {
|
|||
* When false, no events are emitted.
|
||||
*/
|
||||
disable(opts: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
||||
// If parent has been marked artificially dirty we don't want to re-calculate the
|
||||
// parent's dirtiness based on the children.
|
||||
const skipPristineCheck = this._parentMarkedDirty(opts.onlySelf);
|
||||
|
||||
(this as{status: string}).status = DISABLED;
|
||||
(this as{errors: ValidationErrors | null}).errors = null;
|
||||
this._forEachChild(
|
||||
|
@ -506,7 +510,7 @@ export abstract class AbstractControl {
|
|||
(this.statusChanges as EventEmitter<string>).emit(this.status);
|
||||
}
|
||||
|
||||
this._updateAncestors(opts);
|
||||
this._updateAncestors({...opts, skipPristineCheck});
|
||||
this._onDisabledChange.forEach((changeFn) => changeFn(true));
|
||||
}
|
||||
|
||||
|
@ -529,19 +533,26 @@ export abstract class AbstractControl {
|
|||
* When false, no events are emitted.
|
||||
*/
|
||||
enable(opts: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
||||
// If parent has been marked artificially dirty we don't want to re-calculate the
|
||||
// parent's dirtiness based on the children.
|
||||
const skipPristineCheck = this._parentMarkedDirty(opts.onlySelf);
|
||||
|
||||
(this as{status: string}).status = VALID;
|
||||
this._forEachChild(
|
||||
(control: AbstractControl) => { control.enable({...opts, onlySelf: true}); });
|
||||
this.updateValueAndValidity({onlySelf: true, emitEvent: opts.emitEvent});
|
||||
|
||||
this._updateAncestors(opts);
|
||||
this._updateAncestors({...opts, skipPristineCheck});
|
||||
this._onDisabledChange.forEach((changeFn) => changeFn(false));
|
||||
}
|
||||
|
||||
private _updateAncestors(opts: {onlySelf?: boolean, emitEvent?: boolean}) {
|
||||
private _updateAncestors(
|
||||
opts: {onlySelf?: boolean, emitEvent?: boolean, skipPristineCheck?: boolean}) {
|
||||
if (this._parent && !opts.onlySelf) {
|
||||
this._parent.updateValueAndValidity(opts);
|
||||
this._parent._updatePristine();
|
||||
if (!opts.skipPristineCheck) {
|
||||
this._parent._updatePristine();
|
||||
}
|
||||
this._parent._updateTouched();
|
||||
}
|
||||
}
|
||||
|
@ -852,6 +863,16 @@ export abstract class AbstractControl {
|
|||
this._updateOn = (opts as AbstractControlOptions).updateOn !;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if parent has been marked artificially dirty.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
private _parentMarkedDirty(onlySelf?: boolean): boolean {
|
||||
const parentDirty = this._parent && this._parent.dirty;
|
||||
return !onlySelf && parentDirty && !this._parent._anyControlsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1000,6 +1000,37 @@ import {FormArray} from '@angular/forms/src/model';
|
|||
expect(a.value).toEqual(['one']);
|
||||
});
|
||||
|
||||
it('should ignore disabled array controls when determining dirtiness', () => {
|
||||
const c = new FormControl('one');
|
||||
const c2 = new FormControl('two');
|
||||
const a = new FormArray([c, c2]);
|
||||
c.markAsDirty();
|
||||
expect(a.dirty).toBe(true);
|
||||
|
||||
c.disable();
|
||||
expect(c.dirty).toBe(true);
|
||||
expect(a.dirty).toBe(false);
|
||||
|
||||
c.enable();
|
||||
expect(a.dirty).toBe(true);
|
||||
});
|
||||
|
||||
it('should not make a dirty array not dirty when disabling controls', () => {
|
||||
const c = new FormControl('one');
|
||||
const c2 = new FormControl('two');
|
||||
const a = new FormArray([c, c2]);
|
||||
|
||||
a.markAsDirty();
|
||||
expect(a.dirty).toBe(true);
|
||||
expect(c.dirty).toBe(false);
|
||||
|
||||
c.disable();
|
||||
expect(a.dirty).toBe(true);
|
||||
|
||||
c.enable();
|
||||
expect(a.dirty).toBe(true);
|
||||
});
|
||||
|
||||
it('should ignore disabled controls in validation', () => {
|
||||
const c = new FormControl(null, Validators.required);
|
||||
const c2 = new FormControl(null);
|
||||
|
@ -1054,6 +1085,22 @@ import {FormArray} from '@angular/forms/src/model';
|
|||
expect(g.dirty).toBe(true);
|
||||
});
|
||||
|
||||
it('should not make a dirty group not dirty when disabling controls', () => {
|
||||
const c = new FormControl('one');
|
||||
const c2 = new FormControl('two');
|
||||
const g = new FormGroup({one: c, two: c2});
|
||||
|
||||
g.markAsDirty();
|
||||
expect(g.dirty).toBe(true);
|
||||
expect(c.dirty).toBe(false);
|
||||
|
||||
c.disable();
|
||||
expect(g.dirty).toBe(true);
|
||||
|
||||
c.enable();
|
||||
expect(g.dirty).toBe(true);
|
||||
});
|
||||
|
||||
it('should ignore disabled controls when determining touched state', () => {
|
||||
const c = new FormControl('one');
|
||||
const c2 = new FormControl('two');
|
||||
|
|
Loading…
Reference in New Issue