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?


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

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>

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


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

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í


5

Puede seleccionar la identificación usando una función

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

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>

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


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

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>

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>

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

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.