ChatGPT解决这个技术问题 Extra ChatGPT

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

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.
}

M
Mark Rajcok

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


anwere is right but whats the role of ngModel here m still confused will you exaplain it ?
@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.
@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.
@HongboMiao, use [ngValue] if you have an array of objects. Use [value] if you have an array of primitive types (string, boolean, integer).
$event.target.value Property 'value' does not exist on type 'EventTarget'?? can you advice for typescript 4
B
Brocco

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);
}

You're not binding to ngModel here, you're binding to a new variable called device.
whats the roll of [(ngModel)] here
R
Rusty Rob

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>

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.
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.
j
jarisky

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


v
valentine

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


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 .
This no longer works, as of Angular 9+. $event.value is no longer available
A
Abd Abughazaleh

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

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

This doesn't work in newer versions of Angular
B
B.Habibzadeh

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>

That's a material event
k
kravits88

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.


7
7guyo

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);
  }

Is there any other two way binding like NgModel which is not deprecated?
Why do you have the $event parameter in your ts function if you don't use that value?
J
Jérémie Bertrand
<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


A
Alexander

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();
      });
  }

s
skid

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);

}

v
valentine

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);
}

H
HDJEMAI

From angular 7 and up, since ngModel is deprecated, we can simply use the control to get the new selected value very easily in a reactive way.

Example:

<form [formGroup]="frmMyForm">
  <select class="form-control" formControlName="fieldCtrl">
    <option value=""></option>
    <option value="val1">label val 1</option>
    <option value="val2">label val 2</option>
    <option value="val3">label val 3</option>
  </select>
</form>
this.frmMyForm.get('fieldCtrl').valueChanges.subscribe( value => {
  this.selectedValue = value;
});

By using this fully reactive way, we can upgrade older angular application version to newer ones more easily.


t
tinystone

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);
}