refactor(forms): use optional chaining for safer method calls in form directives

Simplifies null checks by leveraging optional chaining when invoking
optional callbacks
This commit is contained in:
SkyZeroZx
2026-02-08 11:47:54 -05:00
committed by Andrew Kushnir
parent e7fa177923
commit 8b3b069be7
10 changed files with 46 additions and 62 deletions
@@ -264,7 +264,7 @@ export abstract class AbstractControlDirective {
* Resets the control with the provided value if the control is present.
*/
reset(value: any = undefined): void {
if (this.control) this.control.reset(value);
this.control?.reset(value);
}
/**
@@ -41,10 +41,8 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn
/** @docs-private */
ngOnDestroy(): void {
if (this.formDirective) {
// Remove the group from its parent group.
this.formDirective.removeFormGroup(this);
}
// Remove the group from its parent group.
this.formDirective?.removeFormGroup(this);
}
/**
+4 -6
View File
@@ -256,9 +256,9 @@ export class NgForm extends ControlContainer implements Form, AfterViewInit {
removeControl(dir: NgModel): void {
resolvedPromise.then(() => {
const container = this._findContainer(dir.path);
if (container) {
container.removeControl(dir.name);
}
container?.removeControl(dir.name);
this._directives.delete(dir);
});
}
@@ -288,9 +288,7 @@ export class NgForm extends ControlContainer implements Form, AfterViewInit {
removeFormGroup(dir: NgModelGroup): void {
resolvedPromise.then(() => {
const container = this._findContainer(dir.path);
if (container) {
container.removeControl(dir.name);
}
container?.removeControl?.(dir.name);
});
}
+1 -1
View File
@@ -278,7 +278,7 @@ export class NgModel extends NgControl implements OnChanges, OnDestroy {
/** @docs-private */
ngOnDestroy(): void {
this.formDirective && this.formDirective.removeControl(this);
this.formDirective?.removeControl(this);
}
/**
@@ -357,24 +357,20 @@ export abstract class AbstractFormDirective
}
private _cleanUpFormContainer(dir: FormArrayName | FormGroupName): void {
if (this.form) {
const ctrl: any = this.form.get(dir.path);
if (ctrl) {
const isControlUpdated = cleanUpFormContainer(ctrl, dir);
if (isControlUpdated) {
// Run validity check only in case a control was updated (i.e. view validators were
// removed) as removing view validators might cause validity to change.
ctrl.updateValueAndValidity({emitEvent: false});
}
const ctrl: any = this.form?.get(dir.path);
if (ctrl) {
const isControlUpdated = cleanUpFormContainer(ctrl, dir);
if (isControlUpdated) {
// Run validity check only in case a control was updated (i.e. view validators were
// removed) as removing view validators might cause validity to change.
ctrl.updateValueAndValidity({emitEvent: false});
}
}
}
private _updateRegistrations() {
this.form._registerOnCollectionChange(this._onCollectionChange);
if (this._oldForm) {
this._oldForm._registerOnCollectionChange(() => {});
}
this._oldForm?._registerOnCollectionChange(() => {});
}
private _updateValidators() {
@@ -179,9 +179,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
/** @docs-private */
ngOnDestroy(): void {
if (this.formDirective) {
this.formDirective.removeControl(this);
}
this.formDirective?.removeControl(this);
}
/**
@@ -286,7 +286,7 @@ export class NgSelectOption implements OnDestroy {
@Input('value')
set value(value: any) {
this._setElementValue(value);
if (this._select) this._select._writeValueAfterRender();
this._select?._writeValueAfterRender();
}
/** @internal */
@@ -296,9 +296,7 @@ export class NgSelectOption implements OnDestroy {
/** @docs-private */
ngOnDestroy(): void {
if (this._select) {
this._select._optionMap.delete(this.id);
this._select._writeValueAfterRender();
}
this._select?._optionMap.delete(this.id);
this._select?._writeValueAfterRender();
}
}
+3 -4
View File
@@ -120,10 +120,9 @@ export function cleanUpControl(
// case. We still check the presence of `valueAccessor` before invoking its methods to make sure
// that cleanup works correctly if app code or tests are setup to ignore the error thrown from
// `selectValueAccessor`. See https://github.com/angular/angular/issues/40521.
if (dir.valueAccessor) {
dir.valueAccessor.registerOnChange(noop);
dir.valueAccessor.registerOnTouched(noop);
}
dir?.valueAccessor?.registerOnChange(noop);
dir?.valueAccessor?.registerOnTouched(noop);
cleanUpValidators(control, dir);
+1 -3
View File
@@ -162,9 +162,7 @@ abstract class AbstractValidatorDirective implements Validator, OnChanges {
const input = this.normalizeInput(changes[this.inputName].currentValue);
this._enabled = this.enabled(input);
this._validator = this._enabled ? this.createValidator(input) : nullValidator;
if (this._onChange) {
this._onChange();
}
this._onChange?.();
}
}
+22 -23
View File
@@ -997,8 +997,8 @@ export abstract class AbstractControl<
this.touched = true;
const sourceControl = opts.sourceControl ?? this;
if (this._parent && !opts.onlySelf) {
this._parent.markAsTouched({...opts, sourceControl});
if (!opts.onlySelf) {
this._parent?.markAsTouched({...opts, sourceControl});
}
if (changed && opts.emitEvent !== false) {
@@ -1087,8 +1087,8 @@ export abstract class AbstractControl<
control.markAsUntouched({onlySelf: true, emitEvent: opts.emitEvent, sourceControl});
});
if (this._parent && !opts.onlySelf) {
this._parent._updateTouched(opts, sourceControl);
if (!opts.onlySelf) {
this._parent?._updateTouched(opts, sourceControl);
}
if (changed && opts.emitEvent !== false) {
@@ -1131,8 +1131,8 @@ export abstract class AbstractControl<
this.pristine = false;
const sourceControl = opts.sourceControl ?? this;
if (this._parent && !opts.onlySelf) {
this._parent.markAsDirty({...opts, sourceControl});
if (!opts.onlySelf) {
this._parent?.markAsDirty({...opts, sourceControl});
}
if (changed && opts.emitEvent !== false) {
@@ -1184,8 +1184,8 @@ export abstract class AbstractControl<
control.markAsPristine({onlySelf: true, emitEvent: opts.emitEvent});
});
if (this._parent && !opts.onlySelf) {
this._parent._updatePristine(opts, sourceControl);
if (!opts.onlySelf) {
this._parent?._updatePristine(opts, sourceControl);
}
if (changed && opts.emitEvent !== false) {
@@ -1230,8 +1230,8 @@ export abstract class AbstractControl<
(this.statusChanges as EventEmitter<FormControlStatus>).emit(this.status);
}
if (this._parent && !opts.onlySelf) {
this._parent.markAsPending({...opts, sourceControl});
if (!opts.onlySelf) {
this._parent?.markAsPending({...opts, sourceControl});
}
}
@@ -1321,12 +1321,12 @@ export abstract class AbstractControl<
opts: {onlySelf?: boolean; emitEvent?: boolean; skipPristineCheck?: boolean},
sourceControl: AbstractControl,
): void {
if (this._parent && !opts.onlySelf) {
this._parent.updateValueAndValidity(opts);
if (!opts.onlySelf) {
this._parent?.updateValueAndValidity(opts);
if (!opts.skipPristineCheck) {
this._parent._updatePristine({}, sourceControl);
this._parent?._updatePristine({}, sourceControl);
}
this._parent._updateTouched({}, sourceControl);
this._parent?._updateTouched({}, sourceControl);
}
}
@@ -1415,8 +1415,8 @@ export abstract class AbstractControl<
(this.statusChanges as EventEmitter<FormControlStatus>).emit(this.status);
}
if (this._parent && !opts.onlySelf) {
this._parent.updateValueAndValidity({...opts, sourceControl});
if (!opts.onlySelf) {
this._parent?.updateValueAndValidity({...opts, sourceControl});
}
}
@@ -1604,7 +1604,7 @@ export abstract class AbstractControl<
*/
getError(errorCode: string, path?: Array<string | number> | string): any {
const control = path ? this.get(path) : this;
return control && control.errors ? control.errors[errorCode] : null;
return control?.errors ? control.errors[errorCode] : null;
}
/**
@@ -1729,8 +1729,8 @@ export abstract class AbstractControl<
const changed = this.pristine !== newPristine;
this.pristine = newPristine;
if (this._parent && !opts.onlySelf) {
this._parent._updatePristine(opts, changedControl);
if (!opts.onlySelf) {
this._parent?._updatePristine(opts, changedControl);
}
if (changed) {
@@ -1743,8 +1743,8 @@ export abstract class AbstractControl<
this.touched = this._anyControlsTouched();
this._events.next(new TouchedChangeEvent(this.touched, changedControl));
if (this._parent && !opts.onlySelf) {
this._parent._updateTouched(opts, changedControl);
if (!opts.onlySelf) {
this._parent?._updateTouched(opts, changedControl);
}
}
@@ -1768,8 +1768,7 @@ export abstract class AbstractControl<
* @internal
*/
private _parentMarkedDirty(onlySelf?: boolean): boolean {
const parentDirty = this._parent && this._parent.dirty;
return !onlySelf && !!parentDirty && !this._parent!._anyControlsDirty();
return !onlySelf && !!this._parent?.dirty && !this._parent!._anyControlsDirty();
}
/** @internal */