Skip to content

Commit

Permalink
perf(forms): make built-in ControlValueAccessors more tree-shakable
Browse files Browse the repository at this point in the history
This commit updates Forms code to avoid direct references to all built-in ControlValueAccessor classes, which
prevents their tree-shaking from production builds. Instead, a new static property is added to all built-in
ControlValueAccessors, which is checked when we need to identify whether a given ControlValueAccessors is a
built-in one.
  • Loading branch information
AndrewKushnir committed Mar 11, 2021
1 parent 41b8f98 commit 557aa6c
Show file tree
Hide file tree
Showing 10 changed files with 51 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@
{
"name": "BROWSER_MODULE_PROVIDERS"
},
{
"name": "BUILTIN_ACCESSORS"
},
{
"name": "BrowserDomAdapter"
},
Expand Down Expand Up @@ -368,9 +365,6 @@
{
"name": "NULL_INJECTOR"
},
{
"name": "NUMBER_VALUE_ACCESSOR"
},
{
"name": "NgControl"
},
Expand Down Expand Up @@ -419,9 +413,6 @@
{
"name": "NullInjector"
},
{
"name": "NumberValueAccessor"
},
{
"name": "ObjectUnsubscribedError"
},
Expand Down Expand Up @@ -455,21 +446,9 @@
{
"name": "R3ViewContainerRef"
},
{
"name": "RADIO_VALUE_ACCESSOR"
},
{
"name": "RANGE_VALUE_ACCESSOR"
},
{
"name": "RadioControlRegistry"
},
{
"name": "RadioControlValueAccessor"
},
{
"name": "RangeValueAccessor"
},
{
"name": "ReactiveFormsComponent"
},
Expand Down Expand Up @@ -509,12 +488,6 @@
{
"name": "SCHEDULER"
},
{
"name": "SELECT_MULTIPLE_VALUE_ACCESSOR"
},
{
"name": "SELECT_VALUE_ACCESSOR"
},
{
"name": "SERVER_TRANSITION_PROVIDERS"
},
Expand All @@ -536,12 +509,6 @@
{
"name": "Sanitizer"
},
{
"name": "SelectControlValueAccessor"
},
{
"name": "SelectMultipleControlValueAccessor"
},
{
"name": "ShadowDomRenderer"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@
{
"name": "BROWSER_MODULE_PROVIDERS"
},
{
"name": "BUILTIN_ACCESSORS"
},
{
"name": "BrowserDomAdapter"
},
Expand Down Expand Up @@ -353,9 +350,6 @@
{
"name": "NULL_INJECTOR"
},
{
"name": "NUMBER_VALUE_ACCESSOR"
},
{
"name": "NgControl"
},
Expand Down Expand Up @@ -413,9 +407,6 @@
{
"name": "NullInjector"
},
{
"name": "NumberValueAccessor"
},
{
"name": "ObjectUnsubscribedError"
},
Expand Down Expand Up @@ -449,24 +440,12 @@
{
"name": "R3ViewContainerRef"
},
{
"name": "RADIO_VALUE_ACCESSOR"
},
{
"name": "RANGE_VALUE_ACCESSOR"
},
{
"name": "REQUIRED_VALIDATOR"
},
{
"name": "RadioControlRegistry"
},
{
"name": "RadioControlValueAccessor"
},
{
"name": "RangeValueAccessor"
},
{
"name": "RecordViewTuple"
},
Expand Down Expand Up @@ -500,12 +479,6 @@
{
"name": "SCHEDULER"
},
{
"name": "SELECT_MULTIPLE_VALUE_ACCESSOR"
},
{
"name": "SELECT_VALUE_ACCESSOR"
},
{
"name": "SERVER_TRANSITION_PROVIDERS"
},
Expand All @@ -527,12 +500,6 @@
{
"name": "Sanitizer"
},
{
"name": "SelectControlValueAccessor"
},
{
"name": "SelectMultipleControlValueAccessor"
},
{
"name": "ShadowDomRenderer"
},
Expand Down
8 changes: 7 additions & 1 deletion packages/forms/src/directives/checkbox_value_accessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import {Directive, ElementRef, forwardRef, Renderer2} from '@angular/core';

import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {BUILTIN_ACCESSOR_MARKER, ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';

export const CHECKBOX_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
Expand Down Expand Up @@ -46,6 +46,12 @@ export const CHECKBOX_VALUE_ACCESSOR: any = {
providers: [CHECKBOX_VALUE_ACCESSOR]
})
export class CheckboxControlValueAccessor implements ControlValueAccessor {
/**
* Internal-only field that indicates that this a built-in ControlValueAccessor.
* @internal
*/
static[BUILTIN_ACCESSOR_MARKER] = true;

/**
* The registered callback function called when a change event occurs on the input element.
* @nodoc
Expand Down
7 changes: 7 additions & 0 deletions packages/forms/src/directives/control_value_accessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,10 @@ export interface ControlValueAccessor {
*/
export const NG_VALUE_ACCESSOR =
new InjectionToken<ReadonlyArray<ControlValueAccessor>>('NgValueAccessor');

/**
* Name of the internal-only field that is present on all built-in ControlValueAccessor (CVA)
* classes. This is needed to make all standard CVAs tree-shakable (to avoid direct references to
* specific classes).
*/
export const BUILTIN_ACCESSOR_MARKER = '__ngBuiltinCVA__';
8 changes: 7 additions & 1 deletion packages/forms/src/directives/number_value_accessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import {Directive, ElementRef, forwardRef, Renderer2} from '@angular/core';

import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {BUILTIN_ACCESSOR_MARKER, ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';

export const NUMBER_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
Expand Down Expand Up @@ -47,6 +47,12 @@ export const NUMBER_VALUE_ACCESSOR: any = {
providers: [NUMBER_VALUE_ACCESSOR]
})
export class NumberValueAccessor implements ControlValueAccessor {
/**
* Internal-only field that indicates that this a built-in ControlValueAccessor.
* @internal
*/
static[BUILTIN_ACCESSOR_MARKER] = true;

/**
* The registered callback function called when a change or input event occurs on the input
* element.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import {Directive, ElementRef, forwardRef, Injectable, Injector, Input, OnDestroy, OnInit, Renderer2} from '@angular/core';

import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {BUILTIN_ACCESSOR_MARKER, ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {NgControl} from './ng_control';

export const RADIO_VALUE_ACCESSOR: any = {
Expand Down Expand Up @@ -101,6 +101,12 @@ export class RadioControlRegistry {
providers: [RADIO_VALUE_ACCESSOR]
})
export class RadioControlValueAccessor implements ControlValueAccessor, OnDestroy, OnInit {
/**
* Internal-only field that indicates that this a built-in ControlValueAccessor.
* @internal
*/
static[BUILTIN_ACCESSOR_MARKER] = true;

/** @internal */
// TODO(issue/24571): remove '!'.
_state!: boolean;
Expand Down
8 changes: 7 additions & 1 deletion packages/forms/src/directives/range_value_accessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import {Directive, ElementRef, forwardRef, Renderer2, StaticProvider} from '@angular/core';

import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {BUILTIN_ACCESSOR_MARKER, ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';

export const RANGE_VALUE_ACCESSOR: StaticProvider = {
provide: NG_VALUE_ACCESSOR,
Expand Down Expand Up @@ -51,6 +51,12 @@ export const RANGE_VALUE_ACCESSOR: StaticProvider = {
providers: [RANGE_VALUE_ACCESSOR]
})
export class RangeValueAccessor implements ControlValueAccessor {
/**
* Internal-only field that indicates that this a built-in ControlValueAccessor.
* @internal
*/
static[BUILTIN_ACCESSOR_MARKER] = true;

/**
* The registered callback function called when a change or input event occurs on the input
* element.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import {Directive, ElementRef, forwardRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider} from '@angular/core';

import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {BUILTIN_ACCESSOR_MARKER, ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';

export const SELECT_VALUE_ACCESSOR: StaticProvider = {
provide: NG_VALUE_ACCESSOR,
Expand Down Expand Up @@ -89,6 +89,12 @@ function _extractId(valueString: string): string {
providers: [SELECT_VALUE_ACCESSOR]
})
export class SelectControlValueAccessor implements ControlValueAccessor {
/**
* Internal-only field that indicates that this a built-in ControlValueAccessor.
* @internal
*/
static[BUILTIN_ACCESSOR_MARKER] = true;

/** @nodoc */
value: any;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import {Directive, ElementRef, forwardRef, Host, Input, OnDestroy, Optional, Renderer2, StaticProvider} from '@angular/core';

import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {BUILTIN_ACCESSOR_MARKER, ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';

export const SELECT_MULTIPLE_VALUE_ACCESSOR: StaticProvider = {
provide: NG_VALUE_ACCESSOR,
Expand Down Expand Up @@ -82,6 +82,12 @@ abstract class HTMLCollection {
providers: [SELECT_MULTIPLE_VALUE_ACCESSOR]
})
export class SelectMultipleControlValueAccessor implements ControlValueAccessor {
/**
* Internal-only field that indicates that this a built-in ControlValueAccessor.
* @internal
*/
static[BUILTIN_ACCESSOR_MARKER] = true;

/**
* The current value.
* @nodoc
Expand Down
19 changes: 2 additions & 17 deletions packages/forms/src/directives/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,12 @@ import {getControlAsyncValidators, getControlValidators, mergeValidators} from '

import {AbstractControlDirective} from './abstract_control_directive';
import {AbstractFormGroupDirective} from './abstract_form_group_directive';
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
import {ControlContainer} from './control_container';
import {ControlValueAccessor} from './control_value_accessor';
import {BUILTIN_ACCESSOR_MARKER, ControlValueAccessor} from './control_value_accessor';
import {DefaultValueAccessor} from './default_value_accessor';
import {NgControl} from './ng_control';
import {NumberValueAccessor} from './number_value_accessor';
import {RadioControlValueAccessor} from './radio_control_value_accessor';
import {RangeValueAccessor} from './range_value_accessor';
import {FormArrayName} from './reactive_directives/form_group_name';
import {ReactiveErrors} from './reactive_errors';
import {SelectControlValueAccessor} from './select_control_value_accessor';
import {SelectMultipleControlValueAccessor} from './select_multiple_control_value_accessor';
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';


Expand Down Expand Up @@ -309,17 +303,8 @@ export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any)
return !Object.is(viewModel, change.currentValue);
}

const BUILTIN_ACCESSORS = [
CheckboxControlValueAccessor,
RangeValueAccessor,
NumberValueAccessor,
SelectControlValueAccessor,
SelectMultipleControlValueAccessor,
RadioControlValueAccessor,
];

export function isBuiltInAccessor(valueAccessor: ControlValueAccessor): boolean {
return BUILTIN_ACCESSORS.some(a => valueAccessor.constructor === a);
return valueAccessor.constructor.hasOwnProperty(BUILTIN_ACCESSOR_MARKER);
}

export function syncPendingControls(form: FormGroup, directives: NgControl[]): void {
Expand Down

0 comments on commit 557aa6c

Please sign in to comment.