/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { Directive, ElementRef, forwardRef, Host, Input, Optional, Renderer2, ɵRuntimeError as RuntimeError } from '@angular/core'; import { BuiltInControlValueAccessor, NG_VALUE_ACCESSOR } from './control_value_accessor'; import * as i0 from "@angular/core"; export const SELECT_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SelectControlValueAccessor), multi: true }; function _buildValueString(id, value) { if (id == null) return `${value}`; if (value && typeof value === 'object') value = 'Object'; return `${id}: ${value}`.slice(0, 50); } function _extractId(valueString) { return valueString.split(':')[0]; } /** * @description * 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 * * ### 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 `` supports `compareWith` input. * `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. * * ```ts * const selectedCountriesControl = new FormControl(); * ``` * * ``` * * * compareFn(c1: Country, c2: Country): boolean { * return c1 && c2 ? c1.id === c2.id : c1 === c2; * } * ``` * * **Note:** We listen to the 'change' event because 'input' events aren't fired * for selects in IE, see: * https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event#browser_compatibility * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ export class SelectControlValueAccessor extends BuiltInControlValueAccessor { constructor() { super(...arguments); /** @internal */ this._optionMap = new Map(); /** @internal */ this._idCounter = 0; this._compareWith = Object.is; } /** * @description * Tracks the option comparison algorithm for tracking identities when * checking for changes. */ set compareWith(fn) { if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw new RuntimeError(1201 /* RuntimeErrorCode.COMPAREWITH_NOT_A_FN */, `compareWith must be a function, but received ${JSON.stringify(fn)}`); } this._compareWith = fn; } /** * Sets the "value" property on the select element. * @nodoc */ writeValue(value) { this.value = value; const id = this._getOptionId(value); const valueString = _buildValueString(id, value); this.setProperty('value', valueString); } /** * Registers a function called when the control value changes. * @nodoc */ registerOnChange(fn) { this.onChange = (valueString) => { this.value = this._getOptionValue(valueString); fn(this.value); }; } /** @internal */ _registerOption() { return (this._idCounter++).toString(); } /** @internal */ _getOptionId(value) { for (const id of Array.from(this._optionMap.keys())) { if (this._compareWith(this._optionMap.get(id), value)) return id; } return null; } /** @internal */ _getOptionValue(valueString) { const id = _extractId(valueString); return this._optionMap.has(id) ? this._optionMap.get(id) : valueString; } } SelectControlValueAccessor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: SelectControlValueAccessor, deps: null, target: i0.ɵɵFactoryTarget.Directive }); SelectControlValueAccessor.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.8", type: SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: { compareWith: "compareWith" }, host: { listeners: { "change": "onChange($event.target.value)", "blur": "onTouched()" } }, providers: [SELECT_VALUE_ACCESSOR], usesInheritance: true, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: SelectControlValueAccessor, decorators: [{ type: Directive, args: [{ selector: 'select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]', host: { '(change)': 'onChange($event.target.value)', '(blur)': 'onTouched()' }, providers: [SELECT_VALUE_ACCESSOR] }] }], propDecorators: { compareWith: [{ type: Input }] } }); /** * @description * Marks `