Relier l'élément de sélection à l'objet en angulaire

409

Je voudrais lier un élément select à une liste d'objets - ce qui est assez simple:

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

Dans ce cas, il semble que ce selectedValueserait un nombre - l'id de l'élément sélectionné.

Cependant, j'aimerais en fait me lier à l'objet pays lui-même afin que ce selectedValuesoit l'objet plutôt que juste l'id. J'ai essayé de changer la valeur de l'option comme ceci:

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

mais cela ne semble pas fonctionner. Il semble placer un objet dans mon selectedValue- mais pas l'objet que j'attends. Vous pouvez le voir dans mon exemple Plunker .

J'ai également essayé de me lier à l'événement change afin de pouvoir définir l'objet moi-même en fonction de l'ID sélectionné; cependant, il semble que l'événement change se déclenche avant la mise à jour du ngModel lié - ce qui signifie que je n'ai pas accès à la nouvelle valeur sélectionnée à ce stade.

Existe-t-il un moyen propre de lier un élément de sélection à un objet avec Angular 2?

RHarris
la source
Je viens de réaliser que mon Plunk fonctionne un peu différemment dans IE vs Chrome. Ni l'un ni l'autre ne fonctionne réellement comme je le souhaite, mais pour info.
RHarris

Réponses:

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

Exemple StackBlitz

REMARQUE: vous pouvez utiliser à la [ngValue]="c"place de [ngValue]="c.id"où c est l'objet pays complet.

[value]="..."prend uniquement en charge les valeurs de chaîne
[ngValue]="..."prend en charge tout type

mise à jour

S'il values'agit d'un objet, l'instance présélectionnée doit être identique à l'une des valeurs.

Voir également la comparaison personnalisée récemment ajoutée https://github.com/angular/angular/issues/13268 disponible depuis 4.0.0-beta.7

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

Prenez soin de si vous souhaitez accéder à l' thisintérieur 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
la source
21
J'ai essayé, mais cela ne semble lier les données que de Dropdown au modèle. Si vous entrez dans la page avec le modèle déjà défini, la liste déroulante n'est pas définie en conséquence ...
Strinder
13
@Strinder, une erreur fréquente consiste à utiliser une autre instance d'objet pour selectedValueque pour c(l'élément par défaut). Un objet différent même avec les mêmes propriétés et valeurs ne fonctionne pas, il doit s'agir de la même instance d'objet.
Günter Zöchbauer
1
@ GünterZöchbauer Ouais. Déjà pensé à la pensée. Il n'y a donc pas de moyen facile de synchroniser directement avec le modèle et une liste de valeurs? = toujours via onChange?
Strinder
1
Bientôt, nous pourrons peut-être comparer les objets ngModel par leur propriété en utilisant la fonction de comparaison personnalisée. Regardez simplement ce numéro: github.com/angular/angular/issues/13268
kub1x
1
C'est toujours facile une fois que vous le savez ;-) mais angular.io/api/forms/NgSelectOption#description contient un lien angular.io/api/forms/SelectControlValueAccessor avec de bons documents.
Günter Zöchbauer
41

Cela pourrait aider:

    <select [(ngModel)]="selectedValue">
          <option *ngFor="#c of countries" [value]="c.id">{{c.name}}</option>
    </select>
Carolina Faedo
la source
5
J'ai utilisé [valeur] au lieu de [ngValue]. Ce n'est pas la même chose. Cela a fonctionné pour moi
Carolina Faedo
2
Erreur sur '#' en angulaire 4
sea-kg
2
Utiliser letau lieu de #@ sea-kg
Ashraful Islam
1
Cette réponse n'a pas la valeur sélectionnée
San Jaisy
20

Vous pouvez le faire aussi sans avoir besoin de l'utiliser [(ngModel)]dans votre <select>tag

Déclarez une variable dans votre fichier ts

toStr = JSON.stringify;

et dans votre modèle faites ceci

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

puis utiliser

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

pour réanalyser la chaîne dans un objet JavaScript valide

Rahul Kumar
la source
1
C'est en effet faisable, mais sur de gros objets deviendra une douleur. En outre, la capacité de détection de changement d'Angular est quelque chose à penser. La sortie d'informations au format json, facilement analysable par les bots, augmente les performances. L'utilisation de la détection de changement d'Angular masque (encapsule) la logique des données et vous assure de vos informations nécessaires. @ Günter Zöchbauer réponse est la façon de le faire en angulaire. :)
Lucaci Andrei
M'a aidé là où j'avais une seule liste et changer une valeur ne devrait pas mettre à jour la suivante, donc cela a aidé à l'utiliser comme hack sans utiliser ngmodel, merci :)
Melvin
Cela fonctionne pour les objets JavaScript simples, mais notez que pour les instances d'une classe, vous perdriez toutes les méthodes qui s'y trouvent.
KhalilRavanna
13

Cela a fonctionné pour moi:

Modèle HTML:

J'ai ajouté (ngModelChange)="selectChange($event)"à mon 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>

Sur component.ts:

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

Vous devez ajouter à component.tscette fonction:

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

Remarque: j'essaie avec [select]="oneOption.id==model.myListOptions.id"et ne fonctionne pas.

============= Une autre façon peut être: =========

Modèle HTML:

J'ai ajouté [compareWith]="compareByOptionIdà mon 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>

Sur component.ts:

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

Vous devez ajouter à component.tscette fonction:

 /* Return true or false if it is the selected */
 compareByOptionId(idFist, idSecond) {
    return idFist && idSecond && idFist.id == idSecond.id;
 }
Jose Carlos Ramos Carmenates
la source
C'est bien si vous souhaitez également gérer l'événement change pour faire quelque chose de plus (comme informer un rappel de changement). Dans ce cas, il vous suffit de placer [ngModel]puis de définir manuellement votre modèle dans votre rappel de modification personnalisé défini dans (ngModelChange).
écraser le
9

Juste au cas où quelqu'un chercherait à faire de même en utilisant des formulaires réactifs:

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

Consultez l'exemple de travail ici

elvin
la source
5

Vous pouvez sélectionner l'ID à l'aide d'une fonction

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

Pour moi, ça fonctionne comme ça, vous pouvez consoler event.target.value.

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

De plus, si rien d'autre des solutions données ne fonctionne, vérifiez si vous avez importé "FormsModule" dans "AppModule", c'était une clé pour moi.

nikola.maksimovic
la source
2

Créer un autre getter pour l'élément sélectionné

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

En ts:

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

Vous pouvez également obtenir la valeur sélectionnée à l'aide de click () en passant la valeur sélectionnée via la fonction

<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
la source
0

utilisez également cette méthode ..

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

Dans app.component.html:

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

Et 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
la source
0

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

cette approche va toujours fonctionner, mais si vous avez une liste dynamique, assurez-vous de la charger avant le modèle

Jack Sowell
la source