Привязка элемента select к объекту в Angular

410

Я хотел бы привязать элемент select к списку объектов - это достаточно просто:

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

В этом случае оказывается, что selectedValueэто будет число - идентификатор выбранного элемента.

Тем не менее, я бы хотел связать сам объект страны, чтобы selectedValueон был объектом, а не идентификатором. Я попытался изменить значение параметра следующим образом:

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

но это не похоже на работу. Кажется, в моем месте находится объект, selectedValueно не тот, который я ожидаю. Вы можете увидеть это в моем примере с Plunker .

Я также попытался привязаться к событию изменения, чтобы я мог сам установить объект на основе выбранного идентификатора; однако, похоже, что событие изменения запускается до обновления связанной ngModel - это означает, что у меня нет доступа к вновь выбранному значению в этой точке.

Есть ли чистый способ привязать выбранный элемент к объекту с Angular 2?

RHarris
источник
Просто понял, что мой Plunk работает немного по-другому в IE против Chrome. Ни один из них на самом деле не работает так, как я хочу, но, к вашему сведению.
RHarris

Ответы:

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

Пример StackBlitz

ПРИМЕЧАНИЕ: вы можете использовать [ngValue]="c"вместо [ngValue]="c.id"где c - полный объект страны.

[value]="..."поддерживает только строковые значения
[ngValue]="..."поддерживает любой тип

Обновить

Если valueобъект является объектом, предварительно выбранный экземпляр должен совпадать с одним из значений.

См. Также недавно добавленное пользовательское сравнение https://github.com/angular/angular/issues/13268, доступное с 4.0.0-beta.7

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

Позаботьтесь, если вы хотите получить доступ thisвнутри 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
Пробовал, но это похоже на привязку данных только от Dropdown к модели. Если вы заходите на страницу с уже установленной моделью, выпадающий список не устанавливается соответствующим образом ...
Strinder
13
@ Строгое частая ошибка - использовать другой экземпляр объекта для selectedValuefor c(элемент по умолчанию). Другой объект, даже с теми же свойствами и значениями, не работает, это должен быть один и тот же экземпляр объекта.
Гюнтер Цохбауэр
1
@ GünterZöchbauer Да. Уже думал о мысли. Так что нет простого способа синхронизации напрямую с моделью и списком значений? = всегда через onChange?
Стриндер
1
Вскоре мы сможем сравнить объекты ngModel по их свойствам с помощью пользовательской функции компаратора. Просто посмотрите этот выпуск: github.com/angular/angular/issues/13268
kub1x
1
Это всегда легко, когда вы это знаете ;-), но angular.io/api/forms/NgSelectOption#description содержит ссылку angular.io/api/forms/SelectControlValueAccessor с хорошими документами.
Гюнтер Цохбауэр
41

Это может помочь:

    <select [(ngModel)]="selectedValue">
          <option *ngFor="#c of countries" [value]="c.id">{{c.name}}</option>
    </select>
Каролина Фаэдо
источник
5
Я использовал [значение] вместо [ngValue]. Это не тоже самое. Это сработало для меня
Каролина Фаэдо
2
Ошибка на «#» в угловых 4
море-кг
2
Используйте letвместо #@ sea-kg
Ашрафул Ислам
1
Этот ответ не получает выбранное значение
San Jaisy
20

Вы можете сделать это тоже без необходимости использовать [(ngModel)]в вашем <select>теге

Объявите переменную в вашем файле TS

toStr = JSON.stringify;

и в вашем шаблоне сделать это

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

а затем использовать

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

проанализировать строку обратно в допустимый объект JavaScript

Рахул Кумар
источник
1
Это действительно выполнимо, но на больших объектах станет боль. Кроме того, следует обратить внимание на подчеркивающую способность Angular обнаруживать изменения. Вывод информации в виде json, легко разбираемой ботами, повышает производительность. Использование обнаружения изменений Angular скрывает (инкапсулирует) логику данных и обеспечивает вас необходимой информацией. @ Günter Zöchbauer ответ - это способ сделать это на Angular. :)
Лукачи Андрей
Помогли мне, где у меня был один список, и изменение одного значения не должно обновлять следующее, поэтому помогло использовать это как хак без использования ngmodel, спасибо :)
Melvin
Это работает для простых объектов JavaScript, но обратите внимание, что для экземпляров класса вы потеряете все методы в нем.
ХалилРаванна
13

Это сработало для меня:

Шаблон HTML:

Я добавил (ngModelChange)="selectChange($event)"в мой 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>

На component.ts:

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

Вам нужно добавить к component.tsэтой функции:

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

Примечание: я пробую [select]="oneOption.id==model.myListOptions.id"и не работаю.

============= Другие способы могут быть: =========

Шаблон HTML:

Я добавил [compareWith]="compareByOptionIdв мой 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>

На component.ts:

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

Вам нужно добавить к component.tsэтой функции:

 /* Return true or false if it is the selected */
 compareByOptionId(idFist, idSecond) {
    return idFist && idSecond && idFist.id == idSecond.id;
 }
Хосе Карлос Рамос Карменатес
источник
Это хорошо, если вы также хотите обработать событие изменения, чтобы сделать что-то дополнительное (например, сообщить обратный вызов изменения). Хотя в этом случае вам нужно всего лишь поставить, [ngModel]а затем установить модель вручную в обратном вызове пользовательских изменений, определенном в (ngModelChange).
раздавить
9

На тот случай, если кто-то хочет сделать то же самое с помощью 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>

Проверьте рабочий пример здесь

Эльвин
источник
5

Вы можете выбрать идентификатор с помощью функции

<option *ngFor="#c of countries" (change)="onchange(c.id)">{{c.name}}</option>
Eng.Gabr
источник
4

Для меня это работает так, вы можете утешить event.target.value.

<select (change) = "ChangeValue($event)" (ngModel)="opt">   
    <option *ngFor=" let opt of titleArr" [value]="opt"></option>
</select>
Шубраншу
источник
2

Кроме того, если ничего из указанных решений не работает, проверьте, не импортировали ли вы «FormsModule» из «AppModule», это было для меня ключом.

nikola.maksimovic
источник
2

Создать еще один геттер для выбранного элемента

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

В тс:

get selectedCountry(){
  let countryId = this.countryForm.controls.country.value;
  let selected = this.countries.find(c=> c.id == countryId);
  return selected;
}
Рафи
источник
1

Вы можете получить выбранное значение также с помощью click (), передав выбранное значение через функцию

<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

используйте этот способ также ..

<h1>My Application</h1>
<select [(ngModel)]="selectedValue">
     <option *ngFor="let c of countries" value="{{c.id}}">{{c.name}}</option>
 </select>
Rathinavel
источник
0

В app.component.html:

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

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

этот подход всегда будет работать, однако, если у вас есть динамический список, убедитесь, что вы загружаете его перед моделью

Джек Соуэлл
источник