diff --git a/packages/forms/src/directives/checkbox_value_accessor.ts b/packages/forms/src/directives/checkbox_value_accessor.ts index a1e8ec0614..9a52622266 100644 --- a/packages/forms/src/directives/checkbox_value_accessor.ts +++ b/packages/forms/src/directives/checkbox_value_accessor.ts @@ -17,17 +17,26 @@ export const CHECKBOX_VALUE_ACCESSOR: any = { }; /** - * The accessor for writing a value and listening to changes on a checkbox input element. + * @description + * A `ControlValueAccessor` for writing a value and listening to changes on a checkbox input + * element. * * @usageNotes - * ### Example * - * ``` - * + * ### Using a checkbox with a reactive form. + * + * The following example shows how to use a checkbox with a reactive form. + * + * ```ts + * const rememberLoginControl = new FormControl(); + * ``` + * + * ``` + * * ``` * - * @ngModule FormsModule * @ngModule ReactiveFormsModule + * @ngModule FormsModule * @publicApi */ @Directive({ @@ -37,17 +46,50 @@ export const CHECKBOX_VALUE_ACCESSOR: any = { providers: [CHECKBOX_VALUE_ACCESSOR] }) export class CheckboxControlValueAccessor implements ControlValueAccessor { + /** + * @description + * The registered callback function called when a change event occurs on the input element. + */ onChange = (_: any) => {}; + + /** + * @description + * The registered callback function called when a blur event occurs on the input element. + */ onTouched = () => {}; constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {} + /** + * Sets the "checked" property on the input element. + * + * @param value The checked value + */ writeValue(value: any): void { this._renderer.setProperty(this._elementRef.nativeElement, 'checked', value); } + + /** + * @description + * Registers a function called when the control value changes. + * + * @param fn The callback function + */ registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; } + + /** + * @description + * Registers a function called when the control is touched. + * + * @param fn The callback function + */ registerOnTouched(fn: () => {}): void { this.onTouched = fn; } + /** + * Sets the "disabled" property on the input element. + * + * @param isDisabled The disabled value + */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); } diff --git a/packages/forms/src/directives/default_value_accessor.ts b/packages/forms/src/directives/default_value_accessor.ts index f4d172d49e..a9e0a8480e 100644 --- a/packages/forms/src/directives/default_value_accessor.ts +++ b/packages/forms/src/directives/default_value_accessor.ts @@ -32,18 +32,28 @@ function _isAndroid(): boolean { export const COMPOSITION_BUFFER_MODE = new InjectionToken('CompositionEventMode'); /** - * The default accessor for writing a value and listening to changes that is used by the - * `NgModel`, `FormControlDirective`, and `FormControlName` directives. + * @description + * The default `ControlValueAccessor` for writing a value and listening to changes on input + * elements. The accessor is used by the `FormControlDirective`, `FormControlName`, and + * `NgModel` directives. * * @usageNotes - * ### Example * - * ``` - * + * ### Using the default value accessor + * + * The following example shows how to use an input element that activates the default value accessor + * (in this case, a text field). + * + * ```ts + * const firstNameControl = new FormControl(); + * ``` + * + * ``` + * * ``` * - * @ngModule FormsModule * @ngModule ReactiveFormsModule + * @ngModule FormsModule * @publicApi */ @Directive({ @@ -61,7 +71,16 @@ export const COMPOSITION_BUFFER_MODE = new InjectionToken('CompositionE providers: [DEFAULT_VALUE_ACCESSOR] }) export class DefaultValueAccessor implements ControlValueAccessor { + /** + * @description + * The registered callback function called when an input event occurs on the input element. + */ onChange = (_: any) => {}; + + /** + * @description + * The registered callback function called when a blur event occurs on the input element. + */ onTouched = () => {}; /** Whether the user is creating a composition string (IME events). */ @@ -75,14 +94,37 @@ export class DefaultValueAccessor implements ControlValueAccessor { } } + /** + * Sets the "value" property on the input element. + * + * @param value The checked value + */ writeValue(value: any): void { const normalizedValue = value == null ? '' : value; this._renderer.setProperty(this._elementRef.nativeElement, 'value', normalizedValue); } + /** + * @description + * Registers a function called when the control value changes. + * + * @param fn The callback function + */ registerOnChange(fn: (_: any) => void): void { this.onChange = fn; } + + /** + * @description + * Registers a function called when the control is touched. + * + * @param fn The callback function + */ registerOnTouched(fn: () => void): void { this.onTouched = fn; } + /** + * Sets the "disabled" property on the input element. + * + * @param isDisabled The disabled value + */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); } diff --git a/packages/forms/src/directives/number_value_accessor.ts b/packages/forms/src/directives/number_value_accessor.ts index 741dfac573..4303c183c6 100644 --- a/packages/forms/src/directives/number_value_accessor.ts +++ b/packages/forms/src/directives/number_value_accessor.ts @@ -17,18 +17,27 @@ export const NUMBER_VALUE_ACCESSOR: any = { }; /** - * The accessor for writing a number value and listening to changes that is used by the - * `NgModel`, `FormControlDirective`, and `FormControlName` directives. + * @description + * The `ControlValueAccessor` for writing a number value and listening to number input changes. + * The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel` + * directives. * * @usageNotes - * ### Example * - * ``` - * + * ### Using a number input with a reactive form. + * + * The following example shows how to use a number input with a reactive form. + * + * ```ts + * const totalCountControl = new FormControl(); + * ``` + * + * ``` + * * ``` * - * @ngModule FormsModule * @ngModule ReactiveFormsModule + * @ngModule FormsModule */ @Directive({ selector: @@ -41,22 +50,55 @@ export const NUMBER_VALUE_ACCESSOR: any = { providers: [NUMBER_VALUE_ACCESSOR] }) export class NumberValueAccessor implements ControlValueAccessor { + /** + * @description + * The registered callback function called when a change or input event occurs on the input + * element. + */ onChange = (_: any) => {}; + + /** + * @description + * The registered callback function called when a blur event occurs on the input element. + */ onTouched = () => {}; constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {} + /** + * Sets the "value" property on the input element. + * + * @param value The checked value + */ writeValue(value: number): void { // The value needs to be normalized for IE9, otherwise it is set to 'null' when null const normalizedValue = value == null ? '' : value; this._renderer.setProperty(this._elementRef.nativeElement, 'value', normalizedValue); } + /** + * @description + * Registers a function called when the control value changes. + * + * @param fn The callback function + */ registerOnChange(fn: (_: number|null) => void): void { this.onChange = (value) => { fn(value == '' ? null : parseFloat(value)); }; } + + /** + * @description + * Registers a function called when the control is touched. + * + * @param fn The callback function + */ registerOnTouched(fn: () => void): void { this.onTouched = fn; } + /** + * Sets the "disabled" property on the input element. + * + * @param isDisabled The disabled value + */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); } diff --git a/packages/forms/src/directives/radio_control_value_accessor.ts b/packages/forms/src/directives/radio_control_value_accessor.ts index 3d88f36ed9..b397bdd204 100644 --- a/packages/forms/src/directives/radio_control_value_accessor.ts +++ b/packages/forms/src/directives/radio_control_value_accessor.ts @@ -18,16 +18,25 @@ export const RADIO_VALUE_ACCESSOR: any = { }; /** - * Internal class used by Angular to uncheck radio buttons with the matching name. + * @description + * Class used by Angular to track radio buttons. For internal use only. */ @Injectable() export class RadioControlRegistry { private _accessors: any[] = []; + /** + * @description + * Adds a control to the internal registry. For internal use only. + */ add(control: NgControl, accessor: RadioControlValueAccessor) { this._accessors.push([control, accessor]); } + /** + * @description + * Removes a control from the internal registry. For internal use only. + */ remove(accessor: RadioControlValueAccessor) { for (let i = this._accessors.length - 1; i >= 0; --i) { if (this._accessors[i][1] === accessor) { @@ -37,6 +46,10 @@ export class RadioControlRegistry { } } + /** + * @description + * Selects a radio button. For internal use only. + */ select(accessor: RadioControlValueAccessor) { this._accessors.forEach((c) => { if (this._isSameGroup(c, accessor) && c[1] !== accessor) { @@ -56,32 +69,22 @@ export class RadioControlRegistry { /** * @description - * - * Writes radio control values and listens to radio control changes. - * - * Used by `NgModel`, `FormControlDirective`, and `FormControlName` - * to keep the view synced with the `FormControl` model. - * - * If you have imported the `FormsModule` or the `ReactiveFormsModule`, this - * value accessor will be active on any radio control that has a form directive. You do - * **not** need to add a special selector to activate it. + * The `ControlValueAccessor` for writing radio control values and listening to radio control + * changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and + * `NgModel` directives. * * @usageNotes - * ### How to use radio buttons with form directives * - * To use radio buttons in a template-driven form, you'll want to ensure that radio buttons - * in the same group have the same `name` attribute. Radio buttons with different `name` - * attributes do not affect each other. + * ### Using radio buttons with reactive form directives * - * {@example forms/ts/radioButtons/radio_button_example.ts region='TemplateDriven'} - * - * When using radio buttons in a reactive form, radio buttons in the same group should have the - * same `formControlName`. You can also add a `name` attribute, but it's optional. + * The follow example shows how to use radio buttons in a reactive form. When using radio buttons in + * a reactive form, radio buttons in the same group should have the same `formControlName`. + * Providing a `name` attribute is optional. * * {@example forms/ts/reactiveRadioButtons/reactive_radio_button_example.ts region='Reactive'} * - * @ngModule FormsModule * @ngModule ReactiveFormsModule + * @ngModule FormsModule * @publicApi */ @Directive({ @@ -101,32 +104,81 @@ export class RadioControlValueAccessor implements ControlValueAccessor, /** @internal */ // TODO(issue/24571): remove '!'. _fn !: Function; + + /** + * @description + * The registered callback function called when a change event occurs on the input element. + */ onChange = () => {}; + + /** + * @description + * The registered callback function called when a blur event occurs on the input element. + */ onTouched = () => {}; + /** + * @description + * Tracks the name of the radio input element. + */ // TODO(issue/24571): remove '!'. @Input() name !: string; + + /** + * @description + * Tracks the name of the `FormControl` bound to the directive. The name corresponds + * to a key in the parent `FormGroup` or `FormArray`. + */ // TODO(issue/24571): remove '!'. @Input() formControlName !: string; + + /** + * @description + * Tracks the value of the radio input element + */ @Input() value: any; constructor( private _renderer: Renderer2, private _elementRef: ElementRef, private _registry: RadioControlRegistry, private _injector: Injector) {} + /** + * @description + * A lifecycle method called when the directive is initialized. For internal use only. + * + * @param changes A object of key/value pairs for the set of changed inputs. + */ ngOnInit(): void { this._control = this._injector.get(NgControl); this._checkName(); this._registry.add(this._control, this); } + /** + * @description + * Lifecycle method called before the directive's instance is destroyed. For internal use only. + * + * @param changes A object of key/value pairs for the set of changed inputs. + */ ngOnDestroy(): void { this._registry.remove(this); } + /** + * @description + * Sets the "checked" property value on the radio input element. + * + * @param value The checked value + */ writeValue(value: any): void { this._state = value === this.value; this._renderer.setProperty(this._elementRef.nativeElement, 'checked', this._state); } + /** + * @description + * Registers a function called when the control value changes. + * + * @param fn The callback function + */ registerOnChange(fn: (_: any) => {}): void { this._fn = fn; this.onChange = () => { @@ -135,10 +187,26 @@ export class RadioControlValueAccessor implements ControlValueAccessor, }; } + /** + * Sets the "value" on the radio input element and unchecks it. + * + * @param value + */ fireUncheck(value: any): void { this.writeValue(value); } + /** + * @description + * Registers a function called when the control is touched. + * + * @param fn The callback function + */ registerOnTouched(fn: () => {}): void { this.onTouched = fn; } + /** + * Sets the "disabled" property on the input element. + * + * @param isDisabled The disabled value + */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); } diff --git a/packages/forms/src/directives/range_value_accessor.ts b/packages/forms/src/directives/range_value_accessor.ts index ba5d75758a..8b11c85536 100644 --- a/packages/forms/src/directives/range_value_accessor.ts +++ b/packages/forms/src/directives/range_value_accessor.ts @@ -17,18 +17,27 @@ export const RANGE_VALUE_ACCESSOR: StaticProvider = { }; /** - * The accessor for writing a range value and listening to changes that is used by the - * `NgModel`, `FormControlDirective`, and `FormControlName` directives. + * @description + * The `ControlValueAccessor` for writing a range value and listening to range input changes. + * The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel` + * directives. * * @usageNotes - * ### Example * - * ``` - * + * ### Using a range input with a reactive form + * + * The following example shows how to use a range input with a reactive form. + * + * ```ts + * const ageControl = new FormControl(); + * ``` + * + * ``` + * * ``` * - * @ngModule FormsModule * @ngModule ReactiveFormsModule + * @ngModule FormsModule */ @Directive({ selector: @@ -41,21 +50,53 @@ export const RANGE_VALUE_ACCESSOR: StaticProvider = { providers: [RANGE_VALUE_ACCESSOR] }) export class RangeValueAccessor implements ControlValueAccessor { + /** + * @description + * The registered callback function called when a change or input event occurs on the input + * element. + */ onChange = (_: any) => {}; + + /** + * @description + * The registered callback function called when a blur event occurs on the input element. + */ onTouched = () => {}; constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {} + /** + * Sets the "value" property on the input element. + * + * @param value The checked value + */ writeValue(value: any): void { this._renderer.setProperty(this._elementRef.nativeElement, 'value', parseFloat(value)); } + /** + * @description + * Registers a function called when the control value changes. + * + * @param fn The callback function + */ registerOnChange(fn: (_: number|null) => void): void { this.onChange = (value) => { fn(value == '' ? null : parseFloat(value)); }; } + /** + * @description + * Registers a function called when the control is touched. + * + * @param fn The callback function + */ registerOnTouched(fn: () => void): void { this.onTouched = fn; } + /** + * Sets the "disabled" property on the range input element. + * + * @param isDisabled The disabled value + */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); } diff --git a/packages/forms/src/directives/select_control_value_accessor.ts b/packages/forms/src/directives/select_control_value_accessor.ts index 92945370ff..a8fa16e8cb 100644 --- a/packages/forms/src/directives/select_control_value_accessor.ts +++ b/packages/forms/src/directives/select_control_value_accessor.ts @@ -28,35 +28,26 @@ function _extractId(valueString: string): string { /** * @description - * - * Writes values and listens to changes on a select element. - * - * Used by `NgModel`, `FormControlDirective`, and `FormControlName` - * to keep the view synced with the `FormControl` model. - * - * If you have imported the `FormsModule` or the `ReactiveFormsModule`, this - * value accessor will be active on any select control that has a form directive. You do - * **not** need to add a special selector to activate it. + * The `ControlValueAccessor` for writing select control values and listening to select control + * changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and + * `NgModel` directives. * * @usageNotes - * ### How to use select controls with form directives + * + * ### Using select controls in a reactive form + * + * The following examples show how to use a select control in a reactive form. + * + * {@example forms/ts/reactiveSelectControl/reactive_select_control_example.ts region='Component'} + * + * ### Using select controls in a template-driven form * * To use a select in a template-driven form, simply add an `ngModel` and a `name` * attribute to the main `` tag. Like in the former example, you have the - * choice of binding to the `value` or `ngValue` property on the select's options. - * - * {@example forms/ts/reactiveSelectControl/reactive_select_control_example.ts region='Component'} - * - * ### Caveat: Option selection + * ### Customizing option selection * * Angular uses object identity to select option. It's possible for the identities of items * to change while the data does not. This can happen, for example, if the items are produced @@ -67,10 +58,12 @@ function _extractId(valueString: string): string { * `compareWith` takes a **function** which has two arguments: `option1` and `option2`. * If `compareWith` is given, Angular selects option by the return value of the function. * - * ### Syntax + * ```ts + * const selectedCountriesControl = new FormControl(); + * ``` * * ``` - * * @@ -81,13 +74,13 @@ function _extractId(valueString: string): string { * } * ``` * - * Note: We listen to the 'change' event because 'input' events aren't fired + * **Note:** We listen to the 'change' event because 'input' events aren't fired * for selects in Firefox and IE: * https://bugzilla.mozilla.org/show_bug.cgi?id=1024350 * https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4660045/ * - * @ngModule FormsModule * @ngModule ReactiveFormsModule + * @ngModule FormsModule * @publicApi */ @Directive({ @@ -103,9 +96,23 @@ export class SelectControlValueAccessor implements ControlValueAccessor { /** @internal */ _idCounter: number = 0; + /** + * @description + * The registered callback function called when a change event occurs on the input element. + */ onChange = (_: any) => {}; + + /** + * @description + * The registered callback function called when a blur event occurs on the input element. + */ onTouched = () => {}; + /** + * @description + * Tracks the option comparison algorithm for tracking identities when + * checking for changes. + */ @Input() set compareWith(fn: (o1: any, o2: any) => boolean) { if (typeof fn !== 'function') { @@ -118,6 +125,12 @@ export class SelectControlValueAccessor implements ControlValueAccessor { constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {} + /** + * Sets the "value" property on the input element. The "selectedIndex" + * property is also set if an ID is provided on the option element. + * + * @param value The checked value + */ writeValue(value: any): void { this.value = value; const id: string|null = this._getOptionId(value); @@ -128,14 +141,32 @@ export class SelectControlValueAccessor implements ControlValueAccessor { this._renderer.setProperty(this._elementRef.nativeElement, 'value', valueString); } + /** + * @description + * Registers a function called when the control value changes. + * + * @param fn The callback function + */ registerOnChange(fn: (value: any) => any): void { this.onChange = (valueString: string) => { this.value = this._getOptionValue(valueString); fn(this.value); }; } + + /** + * @description + * Registers a function called when the control is touched. + * + * @param fn The callback function + */ registerOnTouched(fn: () => any): void { this.onTouched = fn; } + /** + * Sets the "disabled" property on the select input element. + * + * @param isDisabled The disabled value + */ setDisabledState(isDisabled: boolean): void { this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); } @@ -160,17 +191,20 @@ export class SelectControlValueAccessor implements ControlValueAccessor { /** * @description - * * Marks ` + * - * - * compareFn(c1: Country, c2: Country): boolean { - * return c1 && c2 ? c1.id === c2.id : c1 === c2; - * } * ``` + * + * ### Customizing option selection + * + * To customize the default option comparison algorithm, ` - * - * - * ``` - * @ngModule FormsModule * @ngModule ReactiveFormsModule + * @ngModule FormsModule + * @publicApi */ @Directive({selector: 'option'}) export class NgSelectMultipleOption implements OnDestroy { @@ -197,6 +240,11 @@ export class NgSelectMultipleOption implements OnDestroy { } } + /** + * @description + * Tracks the value bound to the option element. Unlike the value binding, + * ngValue supports binding to objects. + */ @Input('ngValue') set ngValue(value: any) { if (this._select == null) return; @@ -205,6 +253,11 @@ export class NgSelectMultipleOption implements OnDestroy { this._select.writeValue(this._select.value); } + /** + * @description + * Tracks simple string values bound to the option element. + * For objects, use the `ngValue` input binding. + */ @Input('value') set value(value: any) { if (this._select) { @@ -226,6 +279,10 @@ export class NgSelectMultipleOption implements OnDestroy { this._renderer.setProperty(this._element.nativeElement, 'selected', selected); } + /** + * @description + * Lifecycle method called before the directive's instance is destroyed. For internal use only. + */ ngOnDestroy(): void { if (this._select) { this._select._optionMap.delete(this.id);