perf(forms): use internal `ngDevMode` flag to tree-shake error messages in prod builds (#37821)
This commit adds a guard before throwing any forms errors. This will tree-shake error messages which cannot be minified. It should also help to reduce the bundle size of the `forms` package in production by ~20%. Closes #37697 PR Close #37821
This commit is contained in:
parent
6e643d9874
commit
201a546af8
|
@ -221,15 +221,6 @@
|
|||
{
|
||||
"name": "FormControlName"
|
||||
},
|
||||
{
|
||||
"name": "FormErrorExamples_formControlName"
|
||||
},
|
||||
{
|
||||
"name": "FormErrorExamples_formGroupName"
|
||||
},
|
||||
{
|
||||
"name": "FormErrorExamples_ngModelGroup"
|
||||
},
|
||||
{
|
||||
"name": "FormGroup"
|
||||
},
|
||||
|
@ -497,9 +488,6 @@
|
|||
{
|
||||
"name": "RangeValueAccessor"
|
||||
},
|
||||
{
|
||||
"name": "ReactiveErrors"
|
||||
},
|
||||
{
|
||||
"name": "ReactiveFormsComponent"
|
||||
},
|
||||
|
@ -608,9 +596,6 @@
|
|||
{
|
||||
"name": "TRANSITION_ID"
|
||||
},
|
||||
{
|
||||
"name": "TemplateDrivenErrors"
|
||||
},
|
||||
{
|
||||
"name": "TemplateFormsComponent"
|
||||
},
|
||||
|
@ -701,9 +686,6 @@
|
|||
{
|
||||
"name": "_keyMap"
|
||||
},
|
||||
{
|
||||
"name": "_noControlError"
|
||||
},
|
||||
{
|
||||
"name": "_randomChar"
|
||||
},
|
||||
|
@ -716,9 +698,6 @@
|
|||
{
|
||||
"name": "_testabilityGetter"
|
||||
},
|
||||
{
|
||||
"name": "_throwError"
|
||||
},
|
||||
{
|
||||
"name": "addComponentLogic"
|
||||
},
|
||||
|
@ -1628,9 +1607,6 @@
|
|||
{
|
||||
"name": "u"
|
||||
},
|
||||
{
|
||||
"name": "unimplemented"
|
||||
},
|
||||
{
|
||||
"name": "unwrapRNode"
|
||||
},
|
||||
|
|
|
@ -13,7 +13,9 @@ import {ControlValueAccessor} from './control_value_accessor';
|
|||
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators';
|
||||
|
||||
function unimplemented(): any {
|
||||
throw new Error('unimplemented');
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
throw new Error('unimplemented');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -317,18 +317,20 @@ export class NgModel extends NgControl implements OnChanges, OnDestroy {
|
|||
}
|
||||
|
||||
private _checkParentType(): void {
|
||||
if (!(this._parent instanceof NgModelGroup) &&
|
||||
this._parent instanceof AbstractFormGroupDirective) {
|
||||
TemplateDrivenErrors.formGroupNameException();
|
||||
} else if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) {
|
||||
TemplateDrivenErrors.modelParentException();
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
if (!(this._parent instanceof NgModelGroup) &&
|
||||
this._parent instanceof AbstractFormGroupDirective) {
|
||||
TemplateDrivenErrors.formGroupNameException();
|
||||
} else if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) {
|
||||
TemplateDrivenErrors.modelParentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _checkName(): void {
|
||||
if (this.options && this.options.name) this.name = this.options.name;
|
||||
|
||||
if (!this._isStandalone() && !this.name) {
|
||||
if (!this._isStandalone() && !this.name && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||
TemplateDrivenErrors.missingNameException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,8 @@ export class NgModelGroup extends AbstractFormGroupDirective implements OnInit,
|
|||
|
||||
/** @internal */
|
||||
_checkParentType(): void {
|
||||
if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) {
|
||||
if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm) &&
|
||||
(typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||
TemplateDrivenErrors.modelGroupParentException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,13 @@ export const RADIO_VALUE_ACCESSOR: any = {
|
|||
multi: true
|
||||
};
|
||||
|
||||
function throwNameError() {
|
||||
throw new Error(`
|
||||
If you define both a name and a formControlName attribute on your radio button, their values
|
||||
must match. Ex: <input type="radio" formControlName="food" name="food">
|
||||
`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Class used by Angular to track radio buttons. For internal use only.
|
||||
|
@ -213,16 +220,10 @@ export class RadioControlValueAccessor implements ControlValueAccessor, OnDestro
|
|||
}
|
||||
|
||||
private _checkName(): void {
|
||||
if (this.name && this.formControlName && this.name !== this.formControlName) {
|
||||
this._throwNameError();
|
||||
if (this.name && this.formControlName && this.name !== this.formControlName &&
|
||||
(typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||
throwNameError();
|
||||
}
|
||||
if (!this.name && this.formControlName) this.name = this.formControlName;
|
||||
}
|
||||
|
||||
private _throwNameError(): void {
|
||||
throw new Error(`
|
||||
If you define both a name and a formControlName attribute on your radio button, their values
|
||||
must match. Ex: <input type="radio" formControlName="food" name="food">
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,11 +68,13 @@ export class FormControlDirective extends NgControl implements OnChanges {
|
|||
|
||||
/**
|
||||
* @description
|
||||
* Triggers a warning that this input should not be used with reactive forms.
|
||||
* Triggers a warning in dev mode that this input should not be used with reactive forms.
|
||||
*/
|
||||
@Input('disabled')
|
||||
set isDisabled(isDisabled: boolean) {
|
||||
ReactiveErrors.disabledAttrWarning();
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
ReactiveErrors.disabledAttrWarning();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kara): remove next 4 properties once deprecation period is over
|
||||
|
|
|
@ -92,11 +92,13 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
|||
|
||||
/**
|
||||
* @description
|
||||
* Triggers a warning that this input should not be used with reactive forms.
|
||||
* Triggers a warning in dev mode that this input should not be used with reactive forms.
|
||||
*/
|
||||
@Input('disabled')
|
||||
set isDisabled(isDisabled: boolean) {
|
||||
ReactiveErrors.disabledAttrWarning();
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
ReactiveErrors.disabledAttrWarning();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kara): remove next 4 properties once deprecation period is over
|
||||
|
@ -212,13 +214,16 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
|||
}
|
||||
|
||||
private _checkParentType(): void {
|
||||
if (!(this._parent instanceof FormGroupName) &&
|
||||
this._parent instanceof AbstractFormGroupDirective) {
|
||||
ReactiveErrors.ngModelGroupException();
|
||||
} else if (
|
||||
!(this._parent instanceof FormGroupName) && !(this._parent instanceof FormGroupDirective) &&
|
||||
!(this._parent instanceof FormArrayName)) {
|
||||
ReactiveErrors.controlParentException();
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
if (!(this._parent instanceof FormGroupName) &&
|
||||
this._parent instanceof AbstractFormGroupDirective) {
|
||||
ReactiveErrors.ngModelGroupException();
|
||||
} else if (
|
||||
!(this._parent instanceof FormGroupName) &&
|
||||
!(this._parent instanceof FormGroupDirective) &&
|
||||
!(this._parent instanceof FormArrayName)) {
|
||||
ReactiveErrors.controlParentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -291,7 +291,7 @@ export class FormGroupDirective extends ControlContainer implements Form, OnChan
|
|||
}
|
||||
|
||||
private _checkFormPresent() {
|
||||
if (!this.form) {
|
||||
if (!this.form && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||
ReactiveErrors.missingFormException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ export class FormGroupName extends AbstractFormGroupDirective implements OnInit,
|
|||
|
||||
/** @internal */
|
||||
_checkParentType(): void {
|
||||
if (_hasInvalidParent(this._parent)) {
|
||||
if (_hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||
ReactiveErrors.groupParentException();
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy
|
|||
}
|
||||
|
||||
private _checkParentType(): void {
|
||||
if (_hasInvalidParent(this._parent)) {
|
||||
if (_hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||
ReactiveErrors.arrayParentException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ export class ReactiveErrors {
|
|||
|
||||
${Examples.ngModelGroup}`);
|
||||
}
|
||||
|
||||
static missingFormException(): void {
|
||||
throw new Error(`formGroup expects a FormGroup instance. Please pass one in.
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
|||
*/
|
||||
@Input()
|
||||
set compareWith(fn: (o1: any, o2: any) => boolean) {
|
||||
if (typeof fn !== 'function') {
|
||||
if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||
throw new Error(`compareWith must be a function, but received ${JSON.stringify(fn)}`);
|
||||
}
|
||||
this._compareWith = fn;
|
||||
|
|
|
@ -112,7 +112,7 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor
|
|||
*/
|
||||
@Input()
|
||||
set compareWith(fn: (o1: any, o2: any) => boolean) {
|
||||
if (typeof fn !== 'function') {
|
||||
if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||
throw new Error(`compareWith must be a function, but received ${JSON.stringify(fn)}`);
|
||||
}
|
||||
this._compareWith = fn;
|
||||
|
|
|
@ -33,8 +33,10 @@ export function controlPath(name: string|null, parent: ControlContainer): string
|
|||
}
|
||||
|
||||
export function setUpControl(control: FormControl, dir: NgControl): void {
|
||||
if (!control) _throwError(dir, 'Cannot find control with');
|
||||
if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with');
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
if (!control) _throwError(dir, 'Cannot find control with');
|
||||
if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with');
|
||||
}
|
||||
|
||||
control.validator = Validators.compose([control.validator!, dir.validator]);
|
||||
control.asyncValidator = Validators.composeAsync([control.asyncValidator!, dir.asyncValidator]);
|
||||
|
@ -64,8 +66,14 @@ export function setUpControl(control: FormControl, dir: NgControl): void {
|
|||
}
|
||||
|
||||
export function cleanUpControl(control: FormControl, dir: NgControl) {
|
||||
dir.valueAccessor!.registerOnChange(() => _noControlError(dir));
|
||||
dir.valueAccessor!.registerOnTouched(() => _noControlError(dir));
|
||||
const noop = () => {
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
_noControlError(dir);
|
||||
}
|
||||
};
|
||||
|
||||
dir.valueAccessor!.registerOnChange(noop);
|
||||
dir.valueAccessor!.registerOnTouched(noop);
|
||||
|
||||
dir._rawValidators.forEach((validator: any) => {
|
||||
if (validator.registerOnValidatorChange) {
|
||||
|
@ -120,7 +128,8 @@ function setUpModelChangePipeline(control: FormControl, dir: NgControl): void {
|
|||
|
||||
export function setUpFormContainer(
|
||||
control: FormGroup|FormArray, dir: AbstractFormGroupDirective|FormArrayName) {
|
||||
if (control == null) _throwError(dir, 'Cannot find control with');
|
||||
if (control == null && (typeof ngDevMode === 'undefined' || ngDevMode))
|
||||
_throwError(dir, 'Cannot find control with');
|
||||
control.validator = Validators.compose([control.validator, dir.validator]);
|
||||
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
|
||||
}
|
||||
|
@ -190,7 +199,7 @@ export function selectValueAccessor(
|
|||
dir: NgControl, valueAccessors: ControlValueAccessor[]): ControlValueAccessor|null {
|
||||
if (!valueAccessors) return null;
|
||||
|
||||
if (!Array.isArray(valueAccessors))
|
||||
if (!Array.isArray(valueAccessors) && (typeof ngDevMode === 'undefined' || ngDevMode))
|
||||
_throwError(dir, 'Value accessor was not provided as an array for form control with');
|
||||
|
||||
let defaultAccessor: ControlValueAccessor|undefined = undefined;
|
||||
|
@ -202,12 +211,12 @@ export function selectValueAccessor(
|
|||
defaultAccessor = v;
|
||||
|
||||
} else if (isBuiltInAccessor(v)) {
|
||||
if (builtinAccessor)
|
||||
if (builtinAccessor && (typeof ngDevMode === 'undefined' || ngDevMode))
|
||||
_throwError(dir, 'More than one built-in value accessor matches form control with');
|
||||
builtinAccessor = v;
|
||||
|
||||
} else {
|
||||
if (customAccessor)
|
||||
if (customAccessor && (typeof ngDevMode === 'undefined' || ngDevMode))
|
||||
_throwError(dir, 'More than one custom value accessor matches form control with');
|
||||
customAccessor = v;
|
||||
}
|
||||
|
@ -217,7 +226,9 @@ export function selectValueAccessor(
|
|||
if (builtinAccessor) return builtinAccessor;
|
||||
if (defaultAccessor) return defaultAccessor;
|
||||
|
||||
_throwError(dir, 'No valid value accessor for form control with');
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
_throwError(dir, 'No valid value accessor for form control with');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -234,7 +245,9 @@ export function _ngModelWarning(
|
|||
|
||||
if (((warningConfig === null || warningConfig === 'once') && !type._ngModelWarningSentOnce) ||
|
||||
(warningConfig === 'always' && !instance._ngModelWarningSent)) {
|
||||
ReactiveErrors.ngModelWarning(name);
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
ReactiveErrors.ngModelWarning(name);
|
||||
}
|
||||
type._ngModelWarningSentOnce = true;
|
||||
instance._ngModelWarningSent = true;
|
||||
}
|
||||
|
|
|
@ -469,7 +469,7 @@ function isPresent(o: any): boolean {
|
|||
|
||||
export function toObservable(r: any): Observable<any> {
|
||||
const obs = isPromise(r) ? from(r) : r;
|
||||
if (!(isObservable(obs))) {
|
||||
if (!(isObservable(obs)) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||
throw new Error(`Expected validator to return Promise or Observable.`);
|
||||
}
|
||||
return obs;
|
||||
|
|
Loading…
Reference in New Issue