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": "FormControlName"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "FormErrorExamples_formControlName"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FormErrorExamples_formGroupName"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FormErrorExamples_ngModelGroup"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "FormGroup"
|
"name": "FormGroup"
|
||||||
},
|
},
|
||||||
@ -497,9 +488,6 @@
|
|||||||
{
|
{
|
||||||
"name": "RangeValueAccessor"
|
"name": "RangeValueAccessor"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "ReactiveErrors"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "ReactiveFormsComponent"
|
"name": "ReactiveFormsComponent"
|
||||||
},
|
},
|
||||||
@ -608,9 +596,6 @@
|
|||||||
{
|
{
|
||||||
"name": "TRANSITION_ID"
|
"name": "TRANSITION_ID"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "TemplateDrivenErrors"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "TemplateFormsComponent"
|
"name": "TemplateFormsComponent"
|
||||||
},
|
},
|
||||||
@ -701,9 +686,6 @@
|
|||||||
{
|
{
|
||||||
"name": "_keyMap"
|
"name": "_keyMap"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "_noControlError"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "_randomChar"
|
"name": "_randomChar"
|
||||||
},
|
},
|
||||||
@ -716,9 +698,6 @@
|
|||||||
{
|
{
|
||||||
"name": "_testabilityGetter"
|
"name": "_testabilityGetter"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "_throwError"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "addComponentLogic"
|
"name": "addComponentLogic"
|
||||||
},
|
},
|
||||||
@ -1628,9 +1607,6 @@
|
|||||||
{
|
{
|
||||||
"name": "u"
|
"name": "u"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "unimplemented"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "unwrapRNode"
|
"name": "unwrapRNode"
|
||||||
},
|
},
|
||||||
|
@ -13,7 +13,9 @@ import {ControlValueAccessor} from './control_value_accessor';
|
|||||||
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators';
|
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators';
|
||||||
|
|
||||||
function unimplemented(): any {
|
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 {
|
private _checkParentType(): void {
|
||||||
if (!(this._parent instanceof NgModelGroup) &&
|
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||||
this._parent instanceof AbstractFormGroupDirective) {
|
if (!(this._parent instanceof NgModelGroup) &&
|
||||||
TemplateDrivenErrors.formGroupNameException();
|
this._parent instanceof AbstractFormGroupDirective) {
|
||||||
} else if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) {
|
TemplateDrivenErrors.formGroupNameException();
|
||||||
TemplateDrivenErrors.modelParentException();
|
} else if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) {
|
||||||
|
TemplateDrivenErrors.modelParentException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _checkName(): void {
|
private _checkName(): void {
|
||||||
if (this.options && this.options.name) this.name = this.options.name;
|
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();
|
TemplateDrivenErrors.missingNameException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,8 @@ export class NgModelGroup extends AbstractFormGroupDirective implements OnInit,
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_checkParentType(): void {
|
_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();
|
TemplateDrivenErrors.modelGroupParentException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,13 @@ export const RADIO_VALUE_ACCESSOR: any = {
|
|||||||
multi: true
|
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
|
* @description
|
||||||
* Class used by Angular to track radio buttons. For internal use only.
|
* 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 {
|
private _checkName(): void {
|
||||||
if (this.name && this.formControlName && this.name !== this.formControlName) {
|
if (this.name && this.formControlName && this.name !== this.formControlName &&
|
||||||
this._throwNameError();
|
(typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||||
|
throwNameError();
|
||||||
}
|
}
|
||||||
if (!this.name && this.formControlName) this.name = this.formControlName;
|
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
|
* @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')
|
@Input('disabled')
|
||||||
set isDisabled(isDisabled: boolean) {
|
set isDisabled(isDisabled: boolean) {
|
||||||
ReactiveErrors.disabledAttrWarning();
|
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||||
|
ReactiveErrors.disabledAttrWarning();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(kara): remove next 4 properties once deprecation period is over
|
// TODO(kara): remove next 4 properties once deprecation period is over
|
||||||
|
@ -92,11 +92,13 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* @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')
|
@Input('disabled')
|
||||||
set isDisabled(isDisabled: boolean) {
|
set isDisabled(isDisabled: boolean) {
|
||||||
ReactiveErrors.disabledAttrWarning();
|
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||||
|
ReactiveErrors.disabledAttrWarning();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(kara): remove next 4 properties once deprecation period is over
|
// 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 {
|
private _checkParentType(): void {
|
||||||
if (!(this._parent instanceof FormGroupName) &&
|
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||||
this._parent instanceof AbstractFormGroupDirective) {
|
if (!(this._parent instanceof FormGroupName) &&
|
||||||
ReactiveErrors.ngModelGroupException();
|
this._parent instanceof AbstractFormGroupDirective) {
|
||||||
} else if (
|
ReactiveErrors.ngModelGroupException();
|
||||||
!(this._parent instanceof FormGroupName) && !(this._parent instanceof FormGroupDirective) &&
|
} else if (
|
||||||
!(this._parent instanceof FormArrayName)) {
|
!(this._parent instanceof FormGroupName) &&
|
||||||
ReactiveErrors.controlParentException();
|
!(this._parent instanceof FormGroupDirective) &&
|
||||||
|
!(this._parent instanceof FormArrayName)) {
|
||||||
|
ReactiveErrors.controlParentException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@ export class FormGroupDirective extends ControlContainer implements Form, OnChan
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _checkFormPresent() {
|
private _checkFormPresent() {
|
||||||
if (!this.form) {
|
if (!this.form && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||||
ReactiveErrors.missingFormException();
|
ReactiveErrors.missingFormException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ export class FormGroupName extends AbstractFormGroupDirective implements OnInit,
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_checkParentType(): void {
|
_checkParentType(): void {
|
||||||
if (_hasInvalidParent(this._parent)) {
|
if (_hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||||
ReactiveErrors.groupParentException();
|
ReactiveErrors.groupParentException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,7 +228,7 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _checkParentType(): void {
|
private _checkParentType(): void {
|
||||||
if (_hasInvalidParent(this._parent)) {
|
if (_hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||||||
ReactiveErrors.arrayParentException();
|
ReactiveErrors.arrayParentException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ export class ReactiveErrors {
|
|||||||
|
|
||||||
${Examples.ngModelGroup}`);
|
${Examples.ngModelGroup}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static missingFormException(): void {
|
static missingFormException(): void {
|
||||||
throw new Error(`formGroup expects a FormGroup instance. Please pass one in.
|
throw new Error(`formGroup expects a FormGroup instance. Please pass one in.
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
|||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
set compareWith(fn: (o1: any, o2: any) => boolean) {
|
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)}`);
|
throw new Error(`compareWith must be a function, but received ${JSON.stringify(fn)}`);
|
||||||
}
|
}
|
||||||
this._compareWith = fn;
|
this._compareWith = fn;
|
||||||
|
@ -112,7 +112,7 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor
|
|||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
set compareWith(fn: (o1: any, o2: any) => boolean) {
|
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)}`);
|
throw new Error(`compareWith must be a function, but received ${JSON.stringify(fn)}`);
|
||||||
}
|
}
|
||||||
this._compareWith = 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 {
|
export function setUpControl(control: FormControl, dir: NgControl): void {
|
||||||
if (!control) _throwError(dir, 'Cannot find control with');
|
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||||
if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with');
|
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.validator = Validators.compose([control.validator!, dir.validator]);
|
||||||
control.asyncValidator = Validators.composeAsync([control.asyncValidator!, dir.asyncValidator]);
|
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) {
|
export function cleanUpControl(control: FormControl, dir: NgControl) {
|
||||||
dir.valueAccessor!.registerOnChange(() => _noControlError(dir));
|
const noop = () => {
|
||||||
dir.valueAccessor!.registerOnTouched(() => _noControlError(dir));
|
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||||
|
_noControlError(dir);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dir.valueAccessor!.registerOnChange(noop);
|
||||||
|
dir.valueAccessor!.registerOnTouched(noop);
|
||||||
|
|
||||||
dir._rawValidators.forEach((validator: any) => {
|
dir._rawValidators.forEach((validator: any) => {
|
||||||
if (validator.registerOnValidatorChange) {
|
if (validator.registerOnValidatorChange) {
|
||||||
@ -120,7 +128,8 @@ function setUpModelChangePipeline(control: FormControl, dir: NgControl): void {
|
|||||||
|
|
||||||
export function setUpFormContainer(
|
export function setUpFormContainer(
|
||||||
control: FormGroup|FormArray, dir: AbstractFormGroupDirective|FormArrayName) {
|
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.validator = Validators.compose([control.validator, dir.validator]);
|
||||||
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
|
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
|
||||||
}
|
}
|
||||||
@ -190,7 +199,7 @@ export function selectValueAccessor(
|
|||||||
dir: NgControl, valueAccessors: ControlValueAccessor[]): ControlValueAccessor|null {
|
dir: NgControl, valueAccessors: ControlValueAccessor[]): ControlValueAccessor|null {
|
||||||
if (!valueAccessors) return 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');
|
_throwError(dir, 'Value accessor was not provided as an array for form control with');
|
||||||
|
|
||||||
let defaultAccessor: ControlValueAccessor|undefined = undefined;
|
let defaultAccessor: ControlValueAccessor|undefined = undefined;
|
||||||
@ -202,12 +211,12 @@ export function selectValueAccessor(
|
|||||||
defaultAccessor = v;
|
defaultAccessor = v;
|
||||||
|
|
||||||
} else if (isBuiltInAccessor(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');
|
_throwError(dir, 'More than one built-in value accessor matches form control with');
|
||||||
builtinAccessor = v;
|
builtinAccessor = v;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (customAccessor)
|
if (customAccessor && (typeof ngDevMode === 'undefined' || ngDevMode))
|
||||||
_throwError(dir, 'More than one custom value accessor matches form control with');
|
_throwError(dir, 'More than one custom value accessor matches form control with');
|
||||||
customAccessor = v;
|
customAccessor = v;
|
||||||
}
|
}
|
||||||
@ -217,7 +226,9 @@ export function selectValueAccessor(
|
|||||||
if (builtinAccessor) return builtinAccessor;
|
if (builtinAccessor) return builtinAccessor;
|
||||||
if (defaultAccessor) return defaultAccessor;
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +245,9 @@ export function _ngModelWarning(
|
|||||||
|
|
||||||
if (((warningConfig === null || warningConfig === 'once') && !type._ngModelWarningSentOnce) ||
|
if (((warningConfig === null || warningConfig === 'once') && !type._ngModelWarningSentOnce) ||
|
||||||
(warningConfig === 'always' && !instance._ngModelWarningSent)) {
|
(warningConfig === 'always' && !instance._ngModelWarningSent)) {
|
||||||
ReactiveErrors.ngModelWarning(name);
|
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||||
|
ReactiveErrors.ngModelWarning(name);
|
||||||
|
}
|
||||||
type._ngModelWarningSentOnce = true;
|
type._ngModelWarningSentOnce = true;
|
||||||
instance._ngModelWarningSent = true;
|
instance._ngModelWarningSent = true;
|
||||||
}
|
}
|
||||||
|
@ -469,7 +469,7 @@ function isPresent(o: any): boolean {
|
|||||||
|
|
||||||
export function toObservable(r: any): Observable<any> {
|
export function toObservable(r: any): Observable<any> {
|
||||||
const obs = isPromise(r) ? from(r) : r;
|
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.`);
|
throw new Error(`Expected validator to return Promise or Observable.`);
|
||||||
}
|
}
|
||||||
return obs;
|
return obs;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user