Enlace de un elemento seleccionado a un objeto en Angular

409

Me gustaría vincular un elemento select a una lista de objetos, lo cual es bastante fácil:

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

En este caso, parece que selectedValuesería un número: la identificación del elemento seleccionado.

Sin embargo, en realidad me gustaría unirme al objeto del país en sí, de modo que ese selectedValuesea ​​el objeto en lugar de solo la identificación. Intenté cambiar el valor de la opción así:

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

Pero esto no parece funcionar. Parece colocar un objeto en mi selectedValue, pero no el objeto que estoy esperando. Puedes ver esto en mi ejemplo Plunker .

También intenté vincular el evento de cambio para poder configurar el objeto yo mismo en función de la identificación seleccionada; sin embargo, parece que el evento de cambio se dispara antes de que se actualice el ngModel vinculado, lo que significa que no tengo acceso al valor recientemente seleccionado en ese punto.

¿Hay una manera limpia de vincular un elemento seleccionado a un objeto con Angular 2?

RHarris
fuente
Me acabo de dar cuenta de que mi Plunk funciona un poco diferente en IE vs. Chrome. Ninguno de los dos funciona como yo quiero, pero para tu información.
RHarris

Respuestas:

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

Ejemplo de StackBlitz

NOTA: puede usar en [ngValue]="c"lugar de [ngValue]="c.id"donde c es el objeto de país completo.

[value]="..."solo admite valores de cadena
[ngValue]="..."admite cualquier tipo

actualizar

Si valuees un objeto, la instancia preseleccionada debe ser idéntica a uno de los valores.

Consulte también la comparación personalizada recientemente agregada https://github.com/angular/angular/issues/13268 disponible desde 4.0.0-beta.7

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

Cuídate si quieres acceder thisdentro 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
fuente
21
Lo probé, pero esto parece vincular los datos solo del menú desplegable al modelo. Si ingresa a la página con el modelo ya configurado, el menú desplegable no se configura en consecuencia ...
Strinder
13
@Strinder un error frecuente es usar otra instancia de objeto para selectedValueque no sea cde (el elemento predeterminado). Un objeto diferente incluso con las mismas propiedades y valores no funciona, tiene que ser la misma instancia de objeto.
Günter Zöchbauer
1
@ GünterZöchbauer Sí. Ya pensé en el pensamiento. Entonces, ¿no hay una manera fácil de sincronizar directamente con el modelo y una lista de valores? = siempre a través de onChange?
Strinder
1
Pronto podremos comparar los objetos ngModel por su propiedad utilizando la función de comparación personalizada. Solo mira este número: github.com/angular/angular/issues/13268
kub1x
1
Siempre es fácil una vez que lo sabes ;-) pero angular.io/api/forms/NgSelectOption#description contiene un enlace angular.io/api/forms/SelectControlValueAccessor con buenos documentos.
Günter Zöchbauer
41

Esto podría ayudar:

    <select [(ngModel)]="selectedValue">
          <option *ngFor="#c of countries" [value]="c.id">{{c.name}}</option>
    </select>
Carolina Faedo
fuente
55
He usado [valor] en lugar de [ngValue]. No es lo mismo. Esto funcionó para mí
Carolina Faedo
2
Error en '#' en angular 4
sea-kg
2
Usar en letlugar de #@ sea-kg
Ashraful Islam
1
Esta respuesta no obtiene el valor seleccionado
San Jaisy
20

También puede hacerlo sin la necesidad de usarlo [(ngModel)]en su <select>etiqueta

Declara una variable en tu archivo ts

toStr = JSON.stringify;

y en tu plantilla haz esto

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

y luego usar

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

para analizar la cadena de nuevo en un objeto JavaScript válido

Rahul Kumar
fuente
1
De hecho, esto es factible, pero en objetos grandes se convertirá en un dolor. Además, la capacidad de subrayar de Angular para la detección de cambios es algo en lo que hay que pensar. La salida de información como json, fácilmente analizable por los bots, se suma a los lances de rendimiento. El uso de la detección de cambios de Angular oculta (encapsula) la lógica de los datos y le asegura la información que necesita. La respuesta de @ Günter Zöchbauer es la forma de hacerlo en Angular. :)
Lucaci Andrei
Me ayudó donde tenía una sola lista y cambiar un valor no debería actualizar el siguiente, por lo que ayudó a usar esto como un truco sin el uso de ngmodel, gracias :)
Melvin
Esto funciona para objetos JavaScript simples, pero tenga en cuenta que para las instancias de una clase perdería todos los métodos en él.
KhalilRavanna
13

A mí me funcionó:

HTML de plantilla:

Agregué (ngModelChange)="selectChange($event)"a mi 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>

En component.ts:

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

Necesitas agregar a component.tsesta función:

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

Nota: Intento con [select]="oneOption.id==model.myListOptions.id"y no funciona.

============= Otras formas pueden ser: =========

HTML de plantilla:

Agregué [compareWith]="compareByOptionIda mi 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>

En component.ts:

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

Necesitas agregar a component.tsesta función:

 /* Return true or false if it is the selected */
 compareByOptionId(idFist, idSecond) {
    return idFist && idSecond && idFist.id == idSecond.id;
 }
José Carlos Ramos Carmenates
fuente
Esto es bueno si también desea manejar el evento de cambio para hacer algo extra (como informar una devolución de llamada de cambio). Aunque en ese caso, solo necesita colocar [ngModel]y luego configurar su modelo manualmente en la devolución de llamada de cambio personalizada definida en (ngModelChange).
aplastar
9

En caso de que alguien esté buscando hacer lo mismo con Formularios reactivos:

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

Mira el ejemplo de trabajo aquí

elvin
fuente
5

Puede seleccionar la identificación usando una función

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

Para mí está funcionando así, puedes consolar event.target.value.

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

Además, si nada más de las soluciones dadas no funciona, verifique si importó "FormsModule" dentro de "AppModule", esa fue una clave para mí.

nikola.maksimovic
fuente
2

Crea otro captador para el artículo seleccionado

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

Puede obtener el valor seleccionado también con la ayuda de click () pasando el valor seleccionado a través de la función

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

use de esta manera también ...

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

En app.component.html:

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

Y 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
fuente
0

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

ese enfoque siempre funcionará, sin embargo, si tiene una lista dinámica, asegúrese de cargarla antes del modelo

Jack Sowell
fuente