SelectControlValueAccessor

directive

npm Package @angular/forms
Module import { SelectControlValueAccessor } from '@angular/forms';
Source forms/src/directives/select_control_value_accessor.ts

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.

Overview

@Directive({
    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]
})
class SelectControlValueAccessor implements ControlValueAccessor {
  value: any
  onChange: (_: any) => { }
  onTouched: () => { }
  set compareWith: (o1: any, o2: any) => boolean
  writeValue(value: any): void
  registerOnChange(fn: (value: any) => any): void
  registerOnTouched(fn: () => any): void
  setDisabledState(isDisabled: boolean): void
}

How To Use

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.

How to use select controls with form directives

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:

import {Component} from '@angular/core';

@Component({
  selector: 'example-app',
  template: `
    <form #f="ngForm">
      <select name="state" ngModel>
        <option value="" disabled>Choose a state</option>
        <option *ngFor="let state of states" [ngValue]="state">
          {{ state.abbrev }}
        </option>
      </select>
    </form>
    
     <p>Form value: {{ f.value | json }}</p>
     <!-- example value: {state: {name: 'New York', abbrev: 'NY'} } -->
  `,
})
export class SelectControlComp {
  states = [
    {name: 'Arizona', abbrev: 'AZ'},
    {name: 'California', abbrev: 'CA'},
    {name: 'Colorado', abbrev: 'CO'},
    {name: 'New York', abbrev: 'NY'},
    {name: 'Pennsylvania', abbrev: 'PA'},
  ];
}

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.

import {Component} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';

@Component({
  selector: 'example-app',
  template: `
    <form [formGroup]="form">
      <select formControlName="state">
        <option *ngFor="let state of states" [ngValue]="state">
          {{ state.abbrev }}
        </option>
      </select>
    </form>
    
     <p>Form value: {{ form.value | json }}</p> 
     <!-- {state: {name: 'New York', abbrev: 'NY'} } -->
  `,
})
export class ReactiveSelectComp {
  states = [
    {name: 'Arizona', abbrev: 'AZ'},
    {name: 'California', abbrev: 'CA'},
    {name: 'Colorado', abbrev: 'CO'},
    {name: 'New York', abbrev: 'NY'},
    {name: 'Pennsylvania', abbrev: 'PA'},
  ];

  form = new FormGroup({
    state: new FormControl(this.states[3]),
  });
}

Caveat: 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 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> 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.

Syntax

<select [compareWith]="compareFn"  [(ngModel)]="selectedCountries">
    <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;
}

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/

  • npm package: @angular/forms

Selectors

select:not([multiple])[formControlName] select:not([multiple])[formControl] select:not([multiple])[ngModel]

Inputs

Constructor

constructor(_renderer: Renderer2, _elementRef: ElementRef)

Members

value: any

onChange: (_: any) => { }

onTouched: () => { }

set compareWith: (o1: any, o2: any) => boolean

writeValue(value: any): void

registerOnChange(fn: (value: any) => any): void

registerOnTouched(fn: () => any): void

setDisabledState(isDisabled: boolean): void

© 2010–2018 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://v5.angular.io/api/forms/SelectControlValueAccessor