How can I get new selection in "select" in Angular 2?
Asked Answered
M

14

513

I am using Angular 2 (TypeScript).

I want to do something with the new selection, but what I get in onChange() is always the last selection. How can I get the new selection?

<select [(ngModel)]="selectedDevice" (change)="onChange($event)">
   <option *ngFor="#i of devices">{{i}}</option>
</select>
onChange($event) {
    console.log(this.selectedDevice);
    // I want to do something here with the new selectedDevice, but what I
    // get here is always the last selection, not the one I just selected.
}
Mental answered 13/11, 2015 at 19:20 Comment(0)
V
955

If you don't need two-way data-binding:

<select (change)="onChange($event.target.value)">
    <option *ngFor="let i of devices">{{i}}</option>
</select>

onChange(deviceValue) {
    console.log(deviceValue);
}

For two-way data-binding, separate the event and property bindings:

<select [ngModel]="selectedDevice" (ngModelChange)="onChange($event)" name="sel2">
    <option [value]="i" *ngFor="let i of devices">{{i}}</option>
</select>
export class AppComponent {
  devices = 'one two three'.split(' ');
  selectedDevice = 'two';
  onChange(newValue) {
    console.log(newValue);
    this.selectedDevice = newValue;
    // ... do other stuff here ...
}

If devices is array of objects, bind to ngValue instead of value:

<select [ngModel]="selectedDeviceObj" (ngModelChange)="onChangeObj($event)" name="sel3">
  <option [ngValue]="i" *ngFor="let i of deviceObjects">{{i.name}}</option>
</select>
{{selectedDeviceObj | json}}
export class AppComponent {
  deviceObjects = [{name: 1}, {name: 2}, {name: 3}];
  selectedDeviceObj = this.deviceObjects[1];
  onChangeObj(newObj) {
    console.log(newObj);
    this.selectedDeviceObj = newObj;
    // ... do other stuff here ...
  }
}

Plunker - does not use <form>
Plunker - uses <form> and uses the new forms API

Vanhook answered 15/11, 2015 at 4:12 Comment(23)
anwere is right but whats the role of ngModel here m still confused will you exaplain it ?Doerrer
@PardeepJain, using two-way data binding with NgModel causes Angular to 1) automatically update selectedDevice when the user selects a different item, and 2) if we set the value of selectedDevice, NgModel will automatically update the view. Since we also want to be notified of a change I added the (change) event binding. We shouldn't have to do it this way... we should be able to break up the two-way data binding into [ngModel]="selectedDevice" and (ngModelChange)="onChange($event)", but as I found out, onChange() gets called twice for each select list change that way.Vanhook
okay thanx one thing more please. i want to get the value of: <option *ngFor="#course of course_array #i=index" #newone value={{course.id}} >{{course.course_name}}</option> course.course_name and course.id both how can i send these two via (change) function ?Doerrer
@PardeepJain, use value=[course] then the change() function will get the entire course object, from which you can then get the individual values.Vanhook
i want to do same thing with materialize.. Tried what you have answered here .. Not WorkingFootman
@Dimple, yeah, well, I looked at the HTML in your plunker, and I see that materialize inserts an <input> into the DOM and uses that in place of the original <select> for controlling the UI, so putting bindings on the select won't help. You would need to use an Angular2-aware version of materialize. (I did a Google search, it looks like there are a few out there.)Vanhook
@MarkRajcok I have a question. For second way, why the $event in (ngModelChange)="onChange($event)" just becomes to newValue automatically in the function below? ThanksMental
@HongboMiao, the special $event variable/symbol is part of the "statement context" that Angular template statements have. If instead I wrote (ngModelChange)="onChange('abc')" then newValue would get the string abc. It's just normal JavaScript function calling.Vanhook
Your plunker does not work in firefox nor IE, are you aware of that?Otis
@MarkRajcok I saw you updated changing from [value] to [ngValue], both work now for arrary. Any difference? thanksMental
@HongboMiao, use [ngValue] if you have an array of objects. Use [value] if you have an array of primitive types (string, boolean, integer).Vanhook
on rc4 and nothing seems working, pathetic! I am getting undefined in my function argument.Rom
@CodeYogi, I updated the plunker to RC4... it works, take a look.Vanhook
@MarkRajcok ah! I see you are not using form. I am using select inside the form, can you please check that?Rom
@CodeYogi, it seems to work inside <form> tags: plnkr.co/edit/pv5j4b1NFyTGFkxHUSga?p=preview. Tested on Chrome only.Vanhook
@MarkRajcok can you please test it on new form API as name attribute is mandatory on control elements. I will try myself to create a plunker.Rom
@CodeYogi, I updated the plunker I referenced in the comment above to use the new form API. I added name="..." to the select elements that use ngModel.Vanhook
[ngModel]="selectedDeviceObj" ngModelChange)="onChangeObj($event)" there's a missing parenthesis, in the third block of code in your answer, thanks btw great answerAlburnum
Update for Material version >= 6 Use the (selectionChange) event on mat-select.Capillary
It still does not work properly in case of 2-way binding for NUMBER values.Episternum
$event.target.value Property 'value' does not exist on type 'EventTarget'?? can you advice for typescript 4Cult
If you're dealing with integer values, it might be worth noting that $event.target.value gives a string, so you might want to use $event.target.valueAsNumber in the template or cast it to a number later in your TypeScript file.Haunted
If $event.target.value doesn't work, you can use a template reference variable and hand over the value of it. <select #selectEl (change)="someFunc(selectEl.value)"></select>Wensleydale
R
45

You can pass the value back into the component by creating a reference variable on the select tag #device and passing it into the change handler onChange($event, device.value) should have the new value

<select [(ng-model)]="selectedDevice" #device (change)="onChange($event, device.value)">
    <option *ng-for="#i of devices">{{i}}</option>
</select>

onChange($event, deviceValue) {
    console.log(deviceValue);
}
Rodrigorodrigue answered 14/11, 2015 at 0:7 Comment(2)
You're not binding to ngModel here, you're binding to a new variable called device.Jato
whats the roll of [(ngModel)] hereDoerrer
M
32

Just use [ngValue] instead of [value]!!

export class Organisation {
  description: string;
  id: string;
  name: string;
}
export class ScheduleComponent implements OnInit {
  selectedOrg: Organisation;
  orgs: Organisation[] = [];

  constructor(private organisationService: OrganisationService) {}

  get selectedOrgMod() {
    return this.selectedOrg;
  }

  set selectedOrgMod(value) {
    this.selectedOrg = value;
  }
}


<div class="form-group">
      <label for="organisation">Organisation
      <select id="organisation" class="form-control" [(ngModel)]="selectedOrgMod" required>
        <option *ngFor="let org of orgs" [ngValue]="org">{{org.name}}</option>
      </select>
      </label>
</div>
Mccary answered 19/5, 2016 at 4:5 Comment(3)
This helped me. Not sure if my case is slightly different, but for me (change)="selectOrg(selectedOrg)" calls the function selectOrg with the current/previous selection, not the newly selected option. I figured out, I don't even need the (change) bit.Mulish
Good spotting. yes that's odd behaviour with (change) passing the old model. I thought it would be like ng-change maybe that's a bug in angular2. I've updated it to use get and set methods. That way in the set selectedOrgMod(value) method I can know that the organisation has changed and update my list of organisational teams.Mccary
works for me in Ang. 15Tintinnabulation
S
16

I ran into this problem while doing the Angular 2 forms tutorial (TypeScript version) at https://angular.io/docs/ts/latest/guide/forms.html

The select/option block wasn't allowing the value of the selection to be changed by selecting one of the options.

Doing what Mark Rajcok suggested worked, although I'm wondering if there's something I missed in the original tutorial or if there was an update. In any case, adding

onChange(newVal) {
    this.model.power = newVal;
}

to hero-form.component.ts in the HeroFormComponent class

and

(change)="onChange($event.target.value)"

to hero-form.component.html in the <select> element made it work

Subinfeudate answered 26/4, 2016 at 16:44 Comment(0)
I
12

use selectionChange in angular 6 and above. example (selectionChange)= onChange($event.value)

Incorporation answered 3/9, 2018 at 14:26 Comment(2)
I didn't use reactive form approach .I just need selected value to trigger service call based on that single value .. above method helped me .Leavings
This no longer works, as of Angular 9+. $event.value is no longer availableUnanimous
G
12

I was has same problem and i solved using the below code :

(change)="onChange($event.target.value)"
Gunning answered 30/6, 2020 at 9:18 Comment(1)
This doesn't work in newer versions of AngularUnanimous
O
7

In Angular 8 you can simply use "selectionChange" like this:

 <mat-select  [(value)]="selectedData" (selectionChange)="onChange()" >
  <mat-option *ngFor="let i of data" [value]="i.ItemID">
  {{i.ItemName}}
  </mat-option>
 </mat-select>
Occasion answered 10/3, 2020 at 8:32 Comment(1)
That's a material eventWirer
E
5

If you don't need two-way data-binding:

<select (change)="updateSorting($event)">
  <option disabled selected>Sorting</option>
  <option value="pointDes">pointDes</option>
  <option value="timeDes">timeDes</option>
  <option value="timeAsc">timeAsc</option>
  <option value="pointAsc">pointAsc</option>
</select>
updateSorting(e: any) {
  // console.log((e.target as HTMLSelectElement)?.value); // also work
  console.log(e.target.value);
}
Enswathe answered 4/5, 2022 at 15:6 Comment(1)
use (selectionChange) instead of (change)Septuple
C
4

Another option is to store the object in value as a string:

<select [ngModel]="selectedDevice | json" (ngModelChange)="onChange($event)">
    <option [value]="i | json" *ngFor="let i of devices">{{i}}</option>
</select>

component:

onChange(val) {
    this.selectedDevice = JSON.parse(val);
}

This was the only way I could get two way binding working to set the select value on page load. This was because my list that populates the select box was not the exact same object as my select was bound to and it needs to be the same object, not just same property values.

Cribble answered 5/2, 2017 at 6:7 Comment(0)
S
4

Angular 7/8

As of angular 6,the use of ngModel input property with reactive forms directive have been deprecated and removed altogether in angular 7+. Read official doc here.

Using reactive form approach you can get/set selected data as;

      //in your template
 <select formControlName="person" (change)="onChange($event)"class="form-control">
    <option [value]="null" disabled>Choose person</option>
      <option *ngFor="let person of persons" [value]="person"> 
        {{person.name}}
    </option>
 </select> 


 //in your ts
 onChange($event) {
    let person = this.peopleForm.get("person").value
    console.log("selected person--->", person);
    // this.peopleForm.get("person").setValue(person.id);
  }
Snoopy answered 23/11, 2019 at 13:5 Comment(2)
Is there any other two way binding like NgModel which is not deprecated?Gdynia
Why do you have the $event parameter in your ts function if you don't use that value?Unanimous
O
2
<mat-form-field>
<mat-select placeholder="Vacancies" [(ngModel)]="vacanciesSpinnerSelectedItem.code" (ngModelChange)="spinnerClick1($event)"
    [ngModelOptions]="{standalone: true}" required>
    <mat-option *ngFor="let spinnerValue of vacanciesSpinnerValues" [value]="spinnerValue?.code">{{spinnerValue.description}}</mat-option>
</mat-select>

I used this for angular Material dropdown. works fine

Oligarch answered 23/2, 2018 at 6:31 Comment(0)
E
1

I tried all the suggestions and nothing works for me.

Imagine the situation: you need a 2-way binding and you have a lookup with NUMBER values and you want to fill your SELECT with the values from this lookup and highlight the chosen option.

Using [value] or (ngModelChange) is a no-go, because you won't be able to select the chosen option after user initiated the change: [value] considers everything a string, as to (ngModelChange) - it obviously should not be used when user initiates the change, so it ruins the proper selection. Using [ngModel] guarantees the fixed format of received VALUE as INDEX: VALUE and it's easy to parse it correspondingly, HOWEVER once again - it ruins the selected option.

So we go with [ngValue] (which will take care of proper types), (change) and... [value], which guarantees the handler receives VALUE, not a DISPLAYED VALUE or INDEX: VALUE :) Below is my working clumsy solution:

  <select
    class="browser-default custom-select"
    (change)="onEdit($event.target.value)"
  >
    <option [value]="">{{
      '::Licences:SelectLicence' | abpLocalization
    }}</option>
    <ng-container *ngIf="licencesLookupData$ | async">
      <option
        *ngFor="let l of licencesLookupData$ | async"
        [ngValue]="l.id"
        [value]="l.id"
        [selected]="l.id == selected.id"
      >
        {{ l.id }} &nbsp;&nbsp; {{ l.displayName | defaultValue }}
      </option>
    </ng-container>
  </select>

  onEdit(idString: string) {
    const id = Number(idString);
    if (isNaN(id)) {
      this.onAdd();
      return;
    }
    this.licencesLoading = true;
    this.licencesService
      .getById(id)
      .pipe(finalize(() => (this.licencesLoading = false)), takeUntil(this.destroy))
      .subscribe((state: Licences.LicenceWithFlatProperties) => {
        this.selected = state;
        this.buildForm();
        this.get();
      });
  }
Episternum answered 25/6, 2020 at 0:11 Comment(0)
O
0

latest ionic 3.2.0 have modified (change) to (ionChange)

eg: HTML

<ion-select (ionChange)="function($event)"> <ion-option>1<ion-option>
</ion-select>

TS

function($event){
// this gives the selected element
 console.log($event);

}
Oculist answered 5/6, 2017 at 19:45 Comment(0)
A
0

In Angular 5 I did with the following way. get the object $event.value instead of $event.target.value

<mat-form-field color="warn">
   <mat-select (ngModelChange)="onChangeTown($event)" class="form-width" formControlName="branch" [(ngModel)]="branch" placeholder="Enter branch">
     <mat-option *ngFor="let branch of branchs" [value]="branch.value">
                  {{ branch.name }}
     </mat-option>
   </mat-select>
</mat-form-field>

onChangeTown(event): void {
  const selectedTown = event;
  console.log('selectedTown: ', selectedTown);
}
Alisonalissa answered 31/5, 2018 at 9:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.