Wiązanie elementu do obiektu w Angular

409

Chciałbym powiązać element select z listą obiektów - co jest dość łatwe:

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

W tym przypadku wydaje się, że selectedValuebyłaby to liczba - identyfikator wybranego elementu.

Jednak tak naprawdę chciałbym powiązać z samym obiektem wiejskim, aby selectedValuebył to obiekt, a nie tylko identyfikator. Próbowałem zmienić wartość opcji tak:

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

ale to nie wydaje się działać. Wydaje się, że umieszcza obiekt w moim selectedValue- ale nie obiekt, którego oczekuję. Możesz to zobaczyć w moim przykładzie Plunkera .

Próbowałem również powiązać zdarzenie zmiany, aby samodzielnie ustawić obiekt na podstawie wybranego identyfikatora; wydaje się jednak, że zdarzenie zmiany jest uruchamiane przed zaktualizowaniem powiązanego modelu ngModel, co oznacza, że ​​w tym momencie nie mam dostępu do nowo wybranej wartości.

Czy istnieje czysty sposób na powiązanie zaznaczonego elementu z obiektem za pomocą Angular 2?

RHarris
źródło
Właśnie zdałem sobie sprawę, że mój Plunk działa trochę inaczej w IE vs. Chrome. Żadne z nich nie działa tak, jak chciałem, ale FYI.
RHarris

Odpowiedzi:

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

Przykład StackBlitz

UWAGA: możesz użyć [ngValue]="c"zamiast [ngValue]="c.id"gdzie c jest kompletnym obiektem kraju.

[value]="..."obsługuje tylko wartości ciągu
[ngValue]="..."obsługuje dowolny typ

aktualizacja

Jeśli valuejest to obiekt, wybrana instancja musi być identyczna z jedną z wartości.

Zobacz także ostatnio dodane niestandardowe porównanie https://github.com/angular/angular/issues/13268 dostępne od 4.0.0-beta.7

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

Dbaj o to, jeśli chcesz uzyskać dostęp thisw ramach 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
źródło
21
Próbowałem, ale wydaje się, że wiąże dane tylko z menu rozwijanego do modelu. Jeśli wchodzenie na stronę z już ustawionym modelem, menu rozwijane nie jest odpowiednio ustawione ...
Strinder
13
@Strinder częstym błędem jest użycie innej instancji obiektu dla selectedValueniż dla c(domyślny element). Inny obiekt nawet z tymi samymi właściwościami i wartościami nie działa, musi to być ta sama instancja obiektu.
Günter Zöchbauer,
1
@ GünterZöchbauer Tak. Już pomyślałem o myśli. Więc nie ma łatwego sposobu synchronizacji bezpośrednio z modelem i listą wartości? = zawsze przez onChange?
Strinder
1
Wkrótce będziemy mogli porównywać obiekty ngModel według ich właściwości za pomocą niestandardowej funkcji komparatora. Wystarczy obejrzeć ten problem: github.com/angular/angular/issues/13268
kub1x
1
Jest to zawsze łatwe, gdy się zorientujesz ;-), ale angular.io/api/forms/NgSelectOption#description zawiera link angular.io/api/forms/SelectControlValueAccessor z dobrymi dokumentami.
Günter Zöchbauer
41

To może pomóc:

    <select [(ngModel)]="selectedValue">
          <option *ngFor="#c of countries" [value]="c.id">{{c.name}}</option>
    </select>
Carolina Faedo
źródło
5
Użyłem [wartość] zamiast [ngValue]. To nie to samo. To zadziałało dla mnie
Carolina Faedo
2
Błąd na „#” w kącie 4
kg morza
2
Użyj letzamiast #@ sea-kg
Ashraful Islam
1
Ta odpowiedź nie otrzymuje wybranej wartości
San Jaisy,
20

Możesz to zrobić również bez potrzeby używania [(ngModel)]w <select>tagu

Zadeklaruj zmienną w swoim pliku ts

toStr = JSON.stringify;

i w tobie zrób to

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

a następnie użyj

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

aby ponownie przeanalizować ciąg znaków w poprawny obiekt JavaScript

Rahul Kumar
źródło
1
Rzeczywiście jest to wykonalne, ale na dużych przedmiotach stanie się bólem. Należy także pomyśleć o podkreślonej przez Angular zdolności wykrywania zmian. Przesyłanie informacji w postaci pliku json, łatwego do przetworzenia przez boty, zwiększa wydajność. Korzystanie z wykrywania zmian Angulara ukrywa (hermetyzuje) logikę danych i zapewnia potrzebne informacje. @ Günter Zöchbauer odpowiedź jest sposobem na zrobienie tego w Angular. :)
Lucaci Andrei
Pomógł mi tam, gdzie miałem jedną listę, a zmiana jednej wartości nie powinna aktualizować następnej, więc pomogło mi to wykorzystać jako hack bez użycia ngmodela, dzięki :)
Melvin
Działa to w przypadku zwykłych obiektów JavaScript, ale pamiętaj, że w przypadku instancji klasy stracisz wszystkie metody.
KhalilRavanna
13

To działało dla mnie:

Szablon HTML:

Dodałem (ngModelChange)="selectChange($event)"do mojego 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>

W pliku component.ts:

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

Musisz dodać do component.tstej funkcji:

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

Uwaga: próbuję [select]="oneOption.id==model.myListOptions.id"i nie pracuję.

============= Inne sposoby to: =========

Szablon HTML:

Dodałem [compareWith]="compareByOptionIddo mojego 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>

W pliku component.ts:

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

Musisz dodać do component.tstej funkcji:

 /* Return true or false if it is the selected */
 compareByOptionId(idFist, idSecond) {
    return idFist && idSecond && idFist.id == idSecond.id;
 }
Jose Carlos Ramos Carmenates
źródło
Jest to dobre, jeśli chcesz również obsłużyć zdarzenie zmiany, aby zrobić coś dodatkowego (na przykład poinformować o oddzwonieniu zmiany). Chociaż w takim przypadku wystarczy wstawić, [ngModel]a następnie ręcznie ustawić model w niestandardowym wywołaniu zwrotnym zmiany zdefiniowanym w (ngModelChange).
zmiażdżyć
9

Na wypadek, gdyby ktoś chciał zrobić to samo przy użyciu formularzy reaktywnych:

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

Sprawdź działający przykład tutaj

elvin
źródło
5

Możesz wybrać identyfikator za pomocą funkcji

<option *ngFor="#c of countries" (change)="onchange(c.id)">{{c.name}}</option>
Eng.Gabr
źródło
4

Dla mnie działa w ten sposób, możesz pocieszyć event.target.value.

<select (change) = "ChangeValue($event)" (ngModel)="opt">   
    <option *ngFor=" let opt of titleArr" [value]="opt"></option>
</select>
Shubhranshu
źródło
2

Ponadto, jeśli nic innego z podanych rozwiązań nie działa, sprawdź, czy zaimportowałeś „FormsModule” do „AppModule”, co było dla mnie kluczem.

nikola.maksimovic
źródło
2

Utwórz kolejny moduł pobierający dla wybranego elementu

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

W ts:

get selectedCountry(){
  let countryId = this.countryForm.controls.country.value;
  let selected = this.countries.find(c=> c.id == countryId);
  return selected;
}
Rafi
źródło
1

Możesz uzyskać wybraną wartość również za pomocą click (), przekazując wybraną wartość przez funkcję

<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
źródło
0

użyj tej metody również ..

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

W app.component.html:

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

I 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
źródło
0

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

takie podejście zawsze będzie działać, jednak jeśli masz listę dynamiczną, upewnij się, że załadowałeś ją przed modelem

Jack Sowell
źródło