Vincular elemento selecionado ao objeto em Angular


409

Gostaria de vincular um elemento select a uma lista de objetos - o que é 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;
}

Nesse caso, parece que selectedValueseria um número - o ID do item selecionado.

No entanto, eu realmente gostaria de vincular ao próprio objeto do país, para que esse selectedValueseja o objeto, e não apenas o ID. Tentei alterar o valor da opção da seguinte forma:

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

mas isso não parece funcionar. Parece colocar um objeto no meu selectedValue- mas não o objeto que estou esperando. Você pode ver isso no meu exemplo do Plunker .

Também tentei vincular o evento de alteração para poder definir o objeto com base no ID selecionado; no entanto, parece que o evento de alteração é acionado antes que o ngModel ligado seja atualizado - o que significa que não tenho acesso ao novo valor selecionado nesse momento.

Existe uma maneira limpa de vincular um elemento de seleção a um objeto com Angular 2?


Acabei de perceber que meu Plunk funciona de maneira um pouco diferente no IE vs. Chrome. Nenhum deles realmente funciona do jeito que eu estou querendo, mas para sua informação.
21316 RHarris

Respostas:


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

Exemplo de StackBlitz

NOTA: você pode usar em [ngValue]="c"vez de [ngValue]="c.id"onde c é o objeto completo do país.

[value]="..."suporta apenas valores de cadeia de caracteres
[ngValue]="..."suporta qualquer tipo

atualizar

Se valuefor um objeto, a instância pré-selecionada precisará ser idêntica a um dos valores.

Consulte também a comparação personalizada adicionada recentemente, https://github.com/angular/angular/issues/13268 disponível desde 4.0.0-beta.7

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

Cuide se você deseja acessar 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
Tentei, mas isso parece vincular dados apenas do menu suspenso para o modelo. Se entrar na página com o modelo já definido, o menu suspenso não está definido adequadamente ...
Strinder 19/07/16

13
@Strinder Um erro frequente é usar outra instância de objeto para selectedValuealém de para c(o item padrão). Um objeto diferente, mesmo com as mesmas propriedades e valores, não funciona, deve ser a mesma instância do objeto.
Günter Zöchbauer

11
@ GünterZöchbauer Yeah. Já pensei em pensamento. Portanto, não há uma maneira fácil de sincronizar diretamente com o modelo e uma lista de valores? = sempre via onChange?
Strinder

11
Em breve, poderemos comparar os objetos ngModel por suas propriedades usando a função comparadora personalizada. Veja esta edição: github.com/angular/angular/issues/13268
kub1x

11
É sempre fácil quando você o conhece ;-) mas angular.io/api/forms/NgSelectOption#description contém um link angular.io/api/forms/SelectControlValueAccessor com bons documentos.
Günter Zöchbauer

41

Isso pode ajudar:

    <select [(ngModel)]="selectedValue">
          <option *ngFor="#c of countries" [value]="c.id">{{c.name}}</option>
    </select>

5
Eu usei [value] em vez de [ngValue]. Não é o mesmo. Isso funcionou para mim
Carolina Faedo

2
Erro no '#' no angular 4
sea-kg

2
Use em letvez de #@ sea-kg
Ashraful Islam

11
Essa resposta não recebe o valor selecionado
San Jaisy

20

Você também pode fazer isso sem a necessidade de usar [(ngModel)]em sua <select>tag

Declare uma variável no seu arquivo ts

toStr = JSON.stringify;

e no seu modelo faça isso

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

e depois use

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

analisar a sequência novamente em um objeto JavaScript válido


11
Isso é realmente factível, mas em objetos grandes se tornará uma dor. Além disso, a capacidade sublinhada da Angular de detecção de alterações é algo a ser pensado. A saída de informações como json, facilmente analisável por bots, aumenta o desempenho. O uso da detecção de alterações da Angular oculta (encapsula) a lógica dos dados e garante as informações necessárias. @ Günter Zöchbauer resposta é a maneira de fazê-lo em Angular. :)
Lucaci Andrei

Ajudou-me onde eu tinha uma única lista e alterando um valor não deve atualizar a próxima para que ele ajudou a usar isso como um hack sem o uso de ngmodel, obrigado :)
Melvin

Isso funciona para objetos JavaScript simples, mas observe que, nas instâncias de uma classe, você perderia todos os métodos nela.
KhalilRavanna

13

Funcionou para mim:

HTML do modelo:

Eu adicionei (ngModelChange)="selectChange($event)"ao meu 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>

Em component.ts:

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

Você precisa adicionar a component.tsesta função:

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

Nota: eu tento com [select]="oneOption.id==model.myListOptions.id" e não trabalho.

============= Outras formas podem ser: =========

HTML do modelo:

Eu adicionei [compareWith]="compareByOptionIdao meu 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>

Em component.ts:

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

Você precisa adicionar a component.tsesta função:

 /* Return true or false if it is the selected */
 compareByOptionId(idFist, idSecond) {
    return idFist && idSecond && idFist.id == idSecond.id;
 }

Isso é bom se você também quiser manipular o evento de alteração para fazer algo extra (como informar um retorno de chamada de alteração). Embora, nesse caso, você só precise colocar [ngModel]e definir seu modelo manualmente no retorno de chamada de alteração personalizado definido em (ngModelChange).
esmagar

9

Apenas no caso de alguém estar olhando para fazer o mesmo usando o Reactive Forms:

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

Veja aqui o exemplo de trabalho


5

Você pode selecionar o ID usando uma função

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

4

Para mim, está funcionando assim, você pode consolar event.target.value.

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

2

Além disso, se nada mais de determinadas soluções não funcionar, verifique se você importou "FormsModule" dentro de "AppModule", isso foi uma chave para mim.


2

Crie outro getter para o item selecionado

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

Em st:

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

1

Você também pode obter o valor selecionado com a ajuda de click () passando o valor selecionado pela função

<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 dessa maneira também ..

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

0

Em app.component.html:

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

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

essa abordagem sempre funcionará, no entanto, se você tiver uma lista dinâmica, carregue-a antes do modelo

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.