docs(forms): update API reference for value accessors (#26946)
PR Close #26946
This commit is contained in:
		
							parent
							
								
									e5c9f7a507
								
							
						
					
					
						commit
						99c5db1fb1
					
				| @ -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 | ||||
|  * | ||||
|  * ``` | ||||
|  * <input type="checkbox" name="rememberLogin" ngModel> | ||||
|  * ### 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(); | ||||
|  * ``` | ||||
|  * | ||||
|  * ``` | ||||
|  * <input type="checkbox" [formControl]="rememberLoginControl"> | ||||
|  * ``` | ||||
|  * | ||||
|  * @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); | ||||
|   } | ||||
|  | ||||
| @ -32,18 +32,28 @@ function _isAndroid(): boolean { | ||||
| export const COMPOSITION_BUFFER_MODE = new InjectionToken<boolean>('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 | ||||
|  * | ||||
|  * ``` | ||||
|  * <input type="text" name="searchQuery" ngModel> | ||||
|  * ### 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(); | ||||
|  * ``` | ||||
|  * | ||||
|  * ``` | ||||
|  * <input type="text" [formControl]="firstNameControl"> | ||||
|  * ``` | ||||
|  * | ||||
|  * @ngModule FormsModule | ||||
|  * @ngModule ReactiveFormsModule | ||||
|  * @ngModule FormsModule | ||||
|  * @publicApi | ||||
|  */ | ||||
| @Directive({ | ||||
| @ -61,7 +71,16 @@ export const COMPOSITION_BUFFER_MODE = new InjectionToken<boolean>('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); | ||||
|   } | ||||
|  | ||||
| @ -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 | ||||
|  * | ||||
|  * ``` | ||||
|  * <input type="number" [(ngModel)]="age"> | ||||
|  * ### 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(); | ||||
|  * ``` | ||||
|  * | ||||
|  * ``` | ||||
|  * <input type="number" [formControl]="totalCountControl"> | ||||
|  * ``` | ||||
|  * | ||||
|  * @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); | ||||
|   } | ||||
|  | ||||
| @ -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); | ||||
|   } | ||||
|  | ||||
| @ -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 | ||||
|  * | ||||
|  * ``` | ||||
|  * <input type="range" [(ngModel)]="age" > | ||||
|  * ### 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(); | ||||
|  * ``` | ||||
|  * | ||||
|  * ``` | ||||
|  * <input type="range" [formControl]="ageControl"> | ||||
|  * ``` | ||||
|  * | ||||
|  * @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); | ||||
|   } | ||||
|  | ||||
| @ -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 `<select>` tag. | ||||
|  * | ||||
|  * If your option values are simple strings, you can bind to the normal `value` property | ||||
|  * on the option.  If your option values happen to be objects (and you'd like to save the | ||||
|  * selection in your form as an object), use `ngValue` instead: | ||||
|  * | ||||
|  * {@example forms/ts/selectControl/select_control_example.ts region='Component'} | ||||
|  * | ||||
|  * In reactive forms, you'll also want to add your form directive (`formControlName` or | ||||
|  * `formControl`) on the main `<select>` 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(); | ||||
|  * ``` | ||||
|  * | ||||
|  * ``` | ||||
|  * <select [compareWith]="compareFn"  [(ngModel)]="selectedCountries"> | ||||
|  * <select [compareWith]="compareFn"  [formControl]="selectedCountriesControl"> | ||||
|  *     <option *ngFor="let country of countries" [ngValue]="country"> | ||||
|  *         {{country.name}} | ||||
|  *     </option> | ||||
| @ -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 `<option>` as dynamic, so Angular can be notified when options change. | ||||
|  * | ||||
|  * See docs for `SelectControlValueAccessor` for usage examples. | ||||
|  * @see `SelectControlValueAccessor` | ||||
|  * | ||||
|  * @ngModule FormsModule | ||||
|  * @ngModule ReactiveFormsModule | ||||
|  * @ngModule FormsModule | ||||
|  * @publicApi | ||||
|  */ | ||||
| @Directive({selector: 'option'}) | ||||
| export class NgSelectOption implements OnDestroy { | ||||
|   /** | ||||
|    * @description | ||||
|    * ID of the option element | ||||
|    */ | ||||
|   // TODO(issue/24571): remove '!'.
 | ||||
|   id !: string; | ||||
| 
 | ||||
| @ -180,6 +214,11 @@ export class NgSelectOption implements OnDestroy { | ||||
|     if (this._select) this.id = this._select._registerOption(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @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; | ||||
| @ -188,6 +227,11 @@ export class NgSelectOption 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) { | ||||
|     this._setElementValue(value); | ||||
| @ -199,6 +243,10 @@ export class NgSelectOption implements OnDestroy { | ||||
|     this._renderer.setProperty(this._element.nativeElement, 'value', value); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @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); | ||||
|  | ||||
| @ -41,33 +41,35 @@ abstract class HTMLCollection { | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * The accessor for writing a value and listening to changes on a select element. | ||||
|  * @description | ||||
|  * The `ControlValueAccessor` for writing multi-select control values and listening to multi-select control | ||||
|  * changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel` | ||||
|  * directives. | ||||
|  *  | ||||
|  * @see `SelectControlValueAccessor` | ||||
|  * | ||||
|  * @usageNotes | ||||
|  * ### Caveat: Options selection | ||||
|  * | ||||
|  * Angular uses object identity to select options. 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 | ||||
|  * from an RPC to the server, and that RPC is re-run. Even if the data hasn't changed, the | ||||
|  * second response will produce objects with different identities. | ||||
|  * | ||||
|  * To customize the default option comparison algorithm, `<select multiple>` supports `compareWith` | ||||
|  * input. `compareWith` takes a **function** which has two arguments: `option1` and `option2`. | ||||
|  * If `compareWith` is given, Angular selects options by the return value of the function. | ||||
|  * | ||||
|  * ### Syntax | ||||
|  *  | ||||
|  * ### Using a multi-select control | ||||
|  *  | ||||
|  * The follow example shows you how to use a multi-select control with a reactive form. | ||||
|  *  | ||||
|  * ```ts
 | ||||
|  * const countryControl = new FormControl(); | ||||
|  * ``` | ||||
|  * | ||||
|  * ``` | ||||
|  * <select multiple [compareWith]="compareFn"  [(ngModel)]="selectedCountries"> | ||||
|  *     <option *ngFor="let country of countries" [ngValue]="country"> | ||||
|  *         {{country.name}} | ||||
|  *     </option> | ||||
|  * <select multiple name="countries" [formControl]="countryControl"> | ||||
|  *   <option *ngFor="let country of countries" [ngValue]="country"> | ||||
|  *     {{ country.name }} | ||||
|  *   </option> | ||||
|  * </select> | ||||
|  * | ||||
|  * 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, `<select>` supports `compareWith` input. | ||||
|  * See the `SelectControlValueAccessor` for usage. | ||||
|  * | ||||
|  * @ngModule ReactiveFormsModule | ||||
|  * @ngModule FormsModule | ||||
| @ -80,15 +82,34 @@ abstract class HTMLCollection { | ||||
|   providers: [SELECT_MULTIPLE_VALUE_ACCESSOR] | ||||
| }) | ||||
| export class SelectMultipleControlValueAccessor implements ControlValueAccessor { | ||||
|   /** | ||||
|    * @description | ||||
|    * The current value | ||||
|    */ | ||||
|   value: any; | ||||
| 
 | ||||
|   /** @internal */ | ||||
|   _optionMap: Map<string, NgSelectMultipleOption> = new Map<string, NgSelectMultipleOption>(); | ||||
|   /** @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') { | ||||
| @ -101,6 +122,13 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor | ||||
| 
 | ||||
|   constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {} | ||||
| 
 | ||||
|   /** | ||||
|    * @description | ||||
|    * Sets the "value" property on one or of more | ||||
|    * of the select's options. | ||||
|    * | ||||
|    * @param value The value | ||||
|    */ | ||||
|   writeValue(value: any): void { | ||||
|     this.value = value; | ||||
|     let optionSelectedStateSetter: (opt: NgSelectMultipleOption, o: any) => void; | ||||
| @ -114,6 +142,13 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor | ||||
|     this._optionMap.forEach(optionSelectedStateSetter); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @description | ||||
|    * Registers a function called when the control value changes | ||||
|    * and writes an array of the selected options. | ||||
|    * | ||||
|    * @param fn The callback function | ||||
|    */ | ||||
|   registerOnChange(fn: (value: any) => any): void { | ||||
|     this.onChange = (_: any) => { | ||||
|       const selected: Array<any> = []; | ||||
| @ -140,8 +175,20 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor | ||||
|       fn(selected); | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @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); | ||||
|   } | ||||
| @ -169,18 +216,14 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * @description | ||||
|  * Marks `<option>` as dynamic, so Angular can be notified when options change. | ||||
|  * | ||||
|  * @usageNotes | ||||
|  * ### Example | ||||
|  * @see `SelectMultipleControlValueAccessor` | ||||
|  * | ||||
|  * ``` | ||||
|  * <select multiple name="city" ngModel> | ||||
|  *   <option *ngFor="let c of cities" [value]="c"></option> | ||||
|  * </select> | ||||
|  * ``` | ||||
|  * @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); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user