Binden eines ausgewählten Elements an ein Objekt in Angular

409

Ich möchte ein ausgewähltes Element an eine Liste von Objekten binden - was einfach genug ist:

@Component({
   selector: 'myApp',
   template: `<h1>My Application</h1>
              <select [(ngModel)]="selectedValue">
                 <option *ngFor="#c of countries" value="c.id">{{c.name}}</option>
              </select>`
})
export class AppComponent{
    countries = [
       {id: 1, name: "United States"},
       {id: 2, name: "Australia"}
       {id: 3, name: "Canada"},
       {id: 4, name: "Brazil"},
       {id: 5, name: "England"}
     ];
    selectedValue = null;
}

In diesem Fall scheint es selectedValuesich um eine Zahl zu handeln - die ID des ausgewählten Elements.

Ich möchte jedoch tatsächlich an das Länderobjekt selbst binden, sodass dies selectedValueeher das Objekt als nur die ID ist. Ich habe versucht, den Wert der Option folgendermaßen zu ändern:

<option *ngFor="#c of countries" value="c">{{c.name}}</option>

aber das scheint nicht zu funktionieren. Es scheint ein Objekt in meinem zu platzieren selectedValue- aber nicht das Objekt, das ich erwarte. Sie können dies in meinem Plunker-Beispiel sehen .

Ich habe auch versucht, eine Bindung an das Änderungsereignis herzustellen, damit ich das Objekt basierend auf der ausgewählten ID selbst festlegen kann. Es scheint jedoch, dass das Änderungsereignis ausgelöst wird, bevor das gebundene ngModel aktualisiert wird. Dies bedeutet, dass ich zu diesem Zeitpunkt keinen Zugriff auf den neu ausgewählten Wert habe.

Gibt es eine saubere Möglichkeit, ein ausgewähltes Element mit Angular 2 an ein Objekt zu binden?

RHarris
quelle
Ich habe gerade festgestellt, dass mein Plunk in IE und Chrome etwas anders funktioniert. Keiner funktioniert tatsächlich so, wie ich es will, aber zu Ihrer Information.
RHarris

Antworten:

734
<h1>My Application</h1>
<select [(ngModel)]="selectedValue">
  <option *ngFor="let c of countries" [ngValue]="c">{{c.name}}</option>
</select>

StackBlitz Beispiel

HINWEIS: Sie können [ngValue]="c"anstelle von verwenden[ngValue]="c.id" c das vollständige Länderobjekt verwenden.

[value]="..." unterstützt nur Zeichenfolgenwerte
[ngValue]="..." unterstützt jeden Typ

aktualisieren

Wenn das valueein Objekt ist, muss die vorausgewählte Instanz mit einem der Werte identisch sein.

Siehe auch den kürzlich hinzugefügten benutzerdefinierten Vergleich https://github.com/angular/angular/issues/13268 , der seit 4.0.0-beta.7 verfügbar ist

<select [compareWith]="compareFn" ...

Achten Sie darauf, ob Sie thisinnerhalb zugreifen möchten compareFn.

compareFn = this._compareFn.bind(this);

// or 
// compareFn = (a, b) => this._compareFn(a, b);

_compareFn(a, b) {
   // Handle compare logic (eg check if unique ids are the same)
   return a.id === b.id;
}
Günter Zöchbauer
quelle
21
Versuchte es, aber dies scheint nur von Dropdown zu Modell Daten zu binden. Wenn Sie die Seite mit bereits eingestelltem Modell betreten, wird das Dropdown-Menü nicht entsprechend eingestellt ...
Strinder
13
@Strinder Ein häufiger Fehler besteht darin, eine andere Objektinstanz selectedValueals für c(das Standardelement) zu verwenden. Ein anderes Objekt funktioniert auch mit denselben Eigenschaften und Werten nicht, es muss dieselbe Objektinstanz sein.
Günter Zöchbauer
1
@ GünterZöchbauer Ja. Schon an Gedanken gedacht. Es gibt also keine einfache Möglichkeit, direkt mit dem Modell und einer Werteliste zu synchronisieren. = immer über onChange?
Strinder
1
Bald können wir möglicherweise die ngModel-Objekte anhand ihrer Eigenschaft mithilfe einer benutzerdefinierten Komparatorfunktion vergleichen.
Sehen Sie sich
1
Es ist immer einfach, wenn Sie es wissen ;-), aber angle.io/api/forms/NgSelectOption#description enthält einen Link angular.io/api/forms/SelectControlValueAccessor mit guten Dokumenten.
Günter Zöchbauer
41

Dies könnte helfen:

    <select [(ngModel)]="selectedValue">
          <option *ngFor="#c of countries" [value]="c.id">{{c.name}}</option>
    </select>
Carolina Faedo
quelle
5
Ich habe [Wert] anstelle von [ngValue] verwendet. Es ist nicht das gleiche. Dies funktionierte für mich
Carolina Faedo
2
Fehler auf '#' in Winkel 4
Meer-kg
2
Verwenden Sie letanstelle von #@ sea-kg
Ashraful Islam
1
Diese Antwort erhält nicht den ausgewählten Wert
San Jaisy
20

Sie können dies auch tun, ohne es [(ngModel)]in Ihrem <select>Tag verwenden zu müssen

Deklarieren Sie eine Variable in Ihrer ts-Datei

toStr = JSON.stringify;

und in Ihrer Vorlage tun Sie dies

 <option *ngFor="let v of values;" [value]="toStr(v)">
      {{v}}
 </option>

und dann verwenden

let value=JSON.parse(event.target.value)

um die Zeichenfolge wieder in ein gültiges JavaScript-Objekt zu analysieren

Rahul Kumar
quelle
1
Dies ist zwar machbar, wird aber bei großen Objekten zum Schmerz. Auch Angulars unterstreichende Fähigkeit zur Änderungserkennung ist zu bedenken. Die Ausgabe von Informationen als json, die von Bots leicht analysiert werden können, trägt zur Leistungssteigerung bei. Durch die Verwendung der Änderungserkennung von Angular wird die Logik der Daten ausgeblendet (eingekapselt) und Sie erhalten die erforderlichen Informationen. @ Günter Zöchbauer Antwort ist der Weg, es in Angular zu tun. :)
Lucaci Andrei
Hat mir geholfen, wo ich eine einzelne Liste hatte und das Ändern eines Werts sollte den nächsten nicht aktualisieren, so dass es hilfreich war, diesen als Hack ohne die Verwendung von ngmodel zu verwenden. Danke :)
Melvin
Dies funktioniert für einfache JavaScript-Objekte. Beachten Sie jedoch, dass Sie für Instanzen einer Klasse alle Methoden verlieren würden.
KhalilRavanna
13

Es hat bei mir funktioniert:

HTML-Vorlage:

Ich fügte (ngModelChange)="selectChange($event)"meinem hinzu select.

<div>
  <label for="myListOptions">My List Options</label>
  <select (ngModelChange)="selectChange($event)" [(ngModel)]=model.myListOptions.id >
    <option *ngFor="let oneOption of listOptions" [ngValue]="oneOption.id">{{oneOption.name}}</option>
  </select>
</div>

Auf component.ts:

  listOptions = [
    { id: 0, name: "Perfect" },
    { id: 1, name: "Low" },
    { id: 2, name: "Minor" },
    { id: 3, name: "High" },
  ];

Und Sie müssen zu component.tsdieser Funktion hinzufügen :

  selectChange( $event) {
    //In my case $event come with a id value
    this.model.myListOptions = this.listOptions[$event];
  }

Hinweis: Ich versuche es mit [select]="oneOption.id==model.myListOptions.id"und arbeite nicht.

============= Eine andere Möglichkeit kann sein: =========

HTML-Vorlage:

Ich fügte [compareWith]="compareByOptionIdmeinem hinzu select.

<div>
  <label for="myListOptions">My List Options</label>
  <select [(ngModel)]=model.myListOptions [compareWith]="compareByOptionId">
    <option *ngFor="let oneOption of listOptions" [ngValue]="oneOption">{{oneOption.name}}</option>
  </select>
</div>

Auf component.ts:

  listOptions = [
    { id: 0, name: "Perfect" },
    { id: 1, name: "Low" },
    { id: 2, name: "Minor" },
    { id: 3, name: "High" },
  ];

Und Sie müssen zu component.tsdieser Funktion hinzufügen :

 /* Return true or false if it is the selected */
 compareByOptionId(idFist, idSecond) {
    return idFist && idSecond && idFist.id == idSecond.id;
 }
Jose Carlos Ramos Carmenates
quelle
Dies ist gut, wenn Sie das Änderungsereignis auch behandeln möchten, um etwas Besonderes zu tun (z. B. einen Änderungsrückruf zu informieren). In diesem Fall müssen Sie [ngModel]Ihr Modell jedoch nur manuell in Ihren in definierten benutzerdefinierten Änderungsrückruf einfügen (ngModelChange).
Crush
9

Nur für den Fall, dass jemand das Gleiche mit reaktiven Formularen tun möchte:

<form [formGroup]="form">
  <select formControlName="country">
    <option *ngFor="let country of countries" [ngValue]="country">{{country.name}}</option>
  </select>
  <p>Selected Country: {{country?.name}}</p>
</form>

Schauen Sie sich das Arbeitsbeispiel hier

Elvin
quelle
5

Sie können die ID mithilfe einer Funktion auswählen

<option *ngFor="#c of countries" (change)="onchange(c.id)">{{c.name}}</option>
Eng.Gabr
quelle
4

Für mich funktioniert es so, Sie können trösten event.target.value.

<select (change) = "ChangeValue($event)" (ngModel)="opt">   
    <option *ngFor=" let opt of titleArr" [value]="opt"></option>
</select>
Shubhranshu
quelle
2

Wenn nichts anderes aus bestimmten Lösungen nicht funktioniert, überprüfen Sie, ob Sie "FormsModule" in "AppModule" importiert haben. Das war ein Schlüssel für mich.

nikola.maksimovic
quelle
2

Erstellen Sie einen weiteren Getter für das ausgewählte Element

<form [formGroup]="countryForm">
  <select formControlName="country">
    <option *ngFor="let c of countries" [value]="c.id">{{c.name}}</option>
  </select>

  <p>Selected Country: {{selectedCountry?.name}}</p>
</form>

In ts:

get selectedCountry(){
  let countryId = this.countryForm.controls.country.value;
  let selected = this.countries.find(c=> c.id == countryId);
  return selected;
}
Rafi
quelle
1

Sie können den ausgewählten Wert auch mit Hilfe von click () abrufen, indem Sie den ausgewählten Wert durch die Funktion übergeben

<md-select placeholder="Select Categorie"  
    name="Select Categorie" >
  <md-option *ngFor="let list of categ" [value]="list.value" (click)="sub_cat(list.category_id)" >
    {{ list.category }}
  </md-option>
</md-select>
Jose Kj
quelle
0

benutze diesen Weg auch ..

<h1>My Application</h1>
<select [(ngModel)]="selectedValue">
     <option *ngFor="let c of countries" value="{{c.id}}">{{c.name}}</option>
 </select>
Rathinavel
quelle
0

In app.component.html:

 <select type="number" [(ngModel)]="selectedLevel">
          <option *ngFor="let level of levels" [ngValue]="level">{{level.name}}</option>
        </select>

Und app.component.ts:

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  levelNum:number;
  levels:Array<Object> = [
      {num: 0, name: "AA"},
      {num: 1, name: "BB"}
  ];

  toNumber(){
    this.levelNum = +this.levelNum;
    console.log(this.levelNum);
  }

  selectedLevel = this.levels[0];

  selectedLevelCustomCompare = {num: 1, name: "BB"}

  compareFn(a, b) {
    console.log(a, b, a && b && a.num == b.num);
    return a && b && a.num == b.num;
  }
}
Mojtaba Nava
quelle
0

<select name="typeFather"
    [(ngModel)]="type.typeFather">
        <option *ngFor="let type of types" [ngValue]="type">{{type.title}}</option>
</select>

Dieser Ansatz wird jedoch immer funktionieren. Wenn Sie eine dynamische Liste haben, stellen Sie sicher, dass Sie diese vor dem Modell laden

Jack Sowell
quelle