Угловой материал: mat-select не выбирает по умолчанию

109

У меня есть mat-select, где параметры - это все объекты, определенные в массиве. Я пытаюсь установить значение по умолчанию для одного из параметров, однако он остается выбранным при отображении страницы.

Мой машинописный файл содержит:

  public options2 = [
    {"id": 1, "name": "a"},
    {"id": 2, "name": "b"}
  ]
  public selected2 = this.options2[1].id;

Мой HTML-файл содержит:

  <div>
    <mat-select
        [(value)]="selected2">
      <mat-option
          *ngFor="let option of options2"
          value="{{ option.id }}">
        {{ option.name }}
      </mat-option>
    </mat-select>
  </div>

Я пробовал установить selected2и valuein mat-optionкак для объекта, так и для его идентификатора, и пробовал использовать оба [(value)]и [(ngModel)]в mat-select, но ни один из них не работает.

Я использую материал версии 2.0.0-beta.10.

Уильям Мур
источник
2
Используйте compareWith. Это более элегантно.
Бадис Мерабет
ДОЛЖЕН compareWithБЫТЬ, см. Ответ badis здесь stackoverflow.com/questions/47333171/…
bresleveloper

Ответы:

144

Используйте привязку для значения в вашем шаблоне.

value="{{ option.id }}"

должно быть

[value]="option.id"

И в выбранном вами значении используйте ngModelвместо value.

<mat-select [(value)]="selected2">

должно быть

<mat-select [(ngModel)]="selected2">

Полный код:

<div>
  <mat-select [(ngModel)]="selected2">
    <mat-option *ngFor="let option of options2" [value]="option.id">{{ option.name }}</mat-option>
  </mat-select>
</div>

На стороне записки , как в версии 2.0.0-beta.12 в материале выбор в настоящее время принимает mat-form-fieldэлемент в качестве родительского элемента , так что согласуются с другим материальным управлением вводом. После обновления замените divэлемент на mat-form-fieldэлемент.

<mat-form-field>
  <mat-select [(ngModel)]="selected2">
    <mat-option *ngFor="let option of options2" [value]="option.id">{{ option.name }}</mat-option>
  </mat-select>
</mat-form-field>
Игорь
источник
10
"Похоже, вы используете ngModel в том же поле формы, что и formControlName. Поддержка использования входного свойства ngModel и события ngModelChange с директивами реактивной формы устарела в Angular v6 и будет удалена в Angular v7. Подробнее об этом , см. наши документы по API здесь: angular.io/api/forms/FormControlName#use-with-ngmodel "
ldgorman
1
@ldgorman - я не понимаю, как вы пришли к такому выводу. Если вы имеете в виду mat-form-field, это ..."used to wrap several Angular Material components and apply common Text field styles", значит, не одно и то же. Кроме этого ОП , а также мой ответ ничего не говорится FormControl, FormGroupили FormControlName.
Игорь
1
У меня такая же проблема даже после реализации того же кода, что и выше @Igor
Chuck
@Chuck - Если у вас все еще возникают проблемы, задайте новый вопрос и включите минимальный воспроизводимый пример . Если вы хотите, чтобы я посмотрел, вы можете ответить на этот комментарий со ссылкой на этот вопрос.
Игорь
2
@ Игорь - Мы разобрались, значение возвращалось в виде числа и Mat-select его в поисках строки. [compareWith]директива - это то, что мы использовали
Чак
94

Используйте compareWithфункцию, чтобы сравнить значения параметра с выбранными значениями. см. здесь: https://material.angular.io/components/select/api#MatSelect

Для объекта следующей структуры:

listOfObjs = [{ name: 'john', id: '1'}, { name: 'jimmy', id: '2'},...]

Определите разметку следующим образом:

<mat-form-field>
  <mat-select
    [compareWith]="compareObjects"
    [(ngModel)]="obj">
       <mat-option  *ngFor="let obj of listOfObjs" [value]="obj">
          {{ obj.name }}
       </mat-option>
    </mat-select>
</mat-form-field>

И определите функцию сравнения следующим образом:

compareObjects(o1: any, o2: any): boolean {
  return o1.name === o2.name && o1.id === o2.id;
}
Бадис Мерабет
источник
8
Идеально подходит для работы с объектами, а не с простыми массивами. Спасибо.
Риаан ван Зил
25

Я использую Angular 5 и реактивные формы с mat-select и не могу получить ни одно из вышеперечисленных решений для отображения начального значения.

Мне пришлось добавить [compareWith], чтобы иметь дело с различными типами, используемыми в компоненте mat-select. Внутри кажется, что mat-select использует массив для хранения выбранного значения. Это, вероятно, позволит одному и тому же коду работать с несколькими вариантами выбора, если этот режим включен.

Angular Select Control Doc

Вот мое решение:

Конструктор форм для инициализации элемента управления формы:

this.formGroup = this.fb.group({
    country: new FormControl([ this.myRecord.country.id ] ),
    ...
});

Затем реализуйте функцию compareWith в своем компоненте:

compareIds(id1: any, id2: any): boolean {
    const a1 = determineId(id1);
    const a2 = determineId(id2);
    return a1 === a2;
}

Затем создайте и экспортируйте функцию defineId (мне пришлось создать отдельную функцию, чтобы mat-select мог ее использовать):

export function determineId(id: any): string {
    if (id.constructor.name === 'array' && id.length > 0) {
       return '' + id[0];
    }
    return '' + id;
}

Наконец, добавьте атрибут compareWith в свой mat-select:

<mat-form-field hintLabel="select one">
<mat-select placeholder="Country" formControlName="country" 
    [compareWith]="compareIds">

    <mat-option>None</mat-option>
    <mat-option *ngFor="let country of countries" [value]="country.id">
                        {{ country.name }}
    </mat-option>
</mat-select>
</mat-form-field>
Вереск92065
источник
Большое спасибо! Найти причину было очень сложно.
Pax Beach
@ Heather92065 Это единственное решение, которое у меня сработало. Я очень благодарен вам!
Latin Warrior
16

Вы должны быть обязательными как [value]в , mat-optionкак показано ниже,

<mat-select placeholder="Panel color" [(value)]="selected2">
  <mat-option *ngFor="let option of options2" [value]="option.id">
    {{ option.name }}
  </mat-option>
</mat-select>

ЖИВАЯ ДЕМО

Аравинд
источник
Это прекрасно работает. Без использования ngModel или setValue () это самый простой и идеальный метод
Rohit Parte
10

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

[compareWith]="compareItems"

Смотрите также документ . Таким образом, полный код будет выглядеть так:

  <div>
    <mat-select
        [(value)]="selected2" [compareWith]="compareItems">
      <mat-option
          *ngFor="let option of options2"
          value="{{ option.id }}">
        {{ option.name }}
      </mat-option>
    </mat-select>
  </div>

и в файле Typescript:

  compareItems(i1, i2) {
    return i1 && i2 && i1.id===i2.id;
  }
Лео
источник
Это сработало для меня, и я думаю, что это в основном правильный способ, но если список содержит только один элемент, он не работает. Спасибо
Code Kadiya
Какого рода исключение вы получите с одним элементом? Потому что сравниваемый следует принимать во заряд , если i1и i2не будет существовать.
LeO
6

Как уже упоминалось в Angular 6, использование ngModel в реактивных формах устарело (и удалено в Angular 7), поэтому я изменил шаблон и компонент следующим образом.

Шаблон:

<mat-form-field>
    <mat-select [formControl]="filter" multiple 
                [compareWith]="compareFn">
        <mat-option *ngFor="let v of values" [value]="v">{{v.label}}</mat-option>
    </mat-select>
</mat-form-field>

Основные части компонента ( onChangesостальные детали опускаются):

interface SelectItem {
    label: string;
    value: any;
}

export class FilterComponent implements OnInit {
    filter = new FormControl();

    @Input
    selected: SelectItem[] = [];

    @Input()
    values: SelectItem[] = [];

    constructor() { }

    ngOnInit() {
        this.filter.setValue(this.selected);
    }

    compareFn(v1: SelectItem, v2: SelectItem): boolean {
        return compareFn(v1, v2);
    }
}

function compareFn(v1: SelectItem, v2: SelectItem): boolean {
    return v1 && v2 ? v1.value === v2.value : v1 === v2;
}

Примечание this.filter.setValue (this.selected) в ngOnInitвыше.

Кажется, работает в Angular 6.

mp31415
источник
На самом деле это должен быть лучший ответ, поскольку он также охватывает выбор объектов при работе с двумя разными результатами API для сравнения.
Марко Кляйн
(например, общий список элементов для выбора и выбранный элемент в другом вызове API).
Марко Кляйн
Angular 7 по-прежнему работает с шаблонными управляемыми моделями! Но вы не можете смешивать его с реактивными формами на одном и том же шаблоне. Ваш намек на это [compareWith]был отличный
LeO
3

Я сделал это так же, как в этих примерах. Пытался установить значение mat-select равным значению одного из mat-options. Но не удалось.

Моя ошибка заключалась в том, чтобы сделать [(значение)] = "someNumberVariable" переменной числового типа, в то время как переменные в mat-options были строками. Даже если бы они выглядели одинаково в шаблоне, он не выбрал бы этот вариант.

Как только я проанализировал someNumberVariable в строке, все было полностью в порядке.

Таким образом, кажется, вам нужно, чтобы значения mat-select и mat-option не только были одинаковыми (если вы представляете числа), но также позволяли им быть строкового типа.

jg80
источник
Это тоже была моя проблема. Одно было числовым, другое - строкой.
Вадим Берман
2

Решение для меня было:

<mat-form-field>
  <mat-select #monedaSelect  formControlName="monedaDebito" [attr.disabled]="isLoading" [placeholder]="monedaLabel | async ">
  <mat-option *ngFor="let moneda of monedasList" [value]="moneda.id">{{moneda.detalle}}</mat-option>
</mat-select>

TS:

@ViewChild('monedaSelect') public monedaSelect: MatSelect;
this.genericService.getOpciones().subscribe(res => {

  this.monedasList = res;
  this.monedaSelect._onChange(res[0].id);


});

Использование объекта: {id: number, detalle: string}

Себа Арсе
источник
1

Попробуй это!

this.selectedObjectList = [{id:1}, {id:2}, {id:3}]
this.allObjectList = [{id:1}, {id:2}, {id:3}, {id:4}, {id:5}]
let newList = this.allObjectList.filter(e => this.selectedObjectList.find(a => e.id == a.id))
this.selectedObjectList = newList
Факу Кинтана
источник
1

Мое решение немного сложнее и проще.

<div>
    <mat-select
        [placeholder]="selected2">
      <mat-option
          *ngFor="let option of options2"
          value="{{ option.id }}">
        {{ option.name }}
      </mat-option>
    </mat-select>
  </div>

Я просто использовал заполнитель . Цвет заполнителя материала по умолчанию - light gray. Чтобы все выглядело так, как будто опция выбрана, я просто изменил CSS следующим образом:

::ng-deep .mat-select-placeholder {
    color: black;
}
Штеффи Керан Рани Дж.
источник
1

Привязка или установка значения по умолчанию работает, только если атрибут значения в MatSelect сопоставим с атрибутом значения, привязанным к MatOption . Если вы привязываете captionсвой элемент к атрибуту value элемента mat-option, вы также должны установить элемент по умолчанию в mat-select для captionвашего элемента. Если вы привязываете Idсвой элемент к mat-option , вы должны также привязать его idк mat-select , а не к целому элементу, заголовку или любому другому, только к тому же полю.

Но делать это нужно с привязкой []

Доминик Медзиньски
источник
1

Я очень внимательно следил за вышеизложенным и все еще не мог выбрать начальное значение.

Причина заключалась в том, что хотя мое связанное значение было определено как строка в машинописном тексте, мой серверный API возвращал число.

Свободная типизация Javascript просто изменяла тип во время выполнения (без ошибок), что не позволяло выбрать начальное значение.

Составная часть

myBoundValue: string;

Шаблон

<mat-select [(ngModel)]="myBoundValue">

Решением было обновить API, чтобы он возвращал строковое значение.

Алекс Купер
источник
1

Очень простой способ добиться этого - использовать a formControlсо значением по умолчанию, например, внутри FormGroup(необязательно). Это пример использования селектора единиц измерения для ввода области:

ts

H_AREA_UNIT = 1;
M_AREA_UNIT = 2;

exampleForm: FormGroup;

this.exampleForm = this.formBuilder.group({
  areaUnit: [this.H_AREA_UNIT],
});

html

<form [formGroup]="exampleForm">
 <mat-form-field>
   <mat-label>Unit</mat-label>
   <mat-select formControlName="areaUnit">
     <mat-option [value]="H_AREA_UNIT">h</mat-option>
     <mat-option [value]="M_AREA_UNIT">m</mat-option>
   </mat-select>
 </mat-form-field>
</form>
Хуан Антонио
источник
0

Сравнение числа и строки обычно ложно , поэтому приведите выбранное значение к строке в ngOnInit, и оно будет работать.

У меня была такая же проблема, я заполнил mat-select перечислением, используя

Object.keys(MyAwesomeEnum).filter(k => !isNaN(Number(k)));

и у меня было значение перечисления, которое я хотел выбрать ...

Я провел несколько часов, пытаясь понять, почему он не работает. И я сделал это сразу после рендеринга всех переменных, используемых в mat-select, коллекции ключей и выбранном ... если у вас есть ["0", "1", "2"] и вы хотите выбрать 1 ( это число) 1 == "1" ложно, и поэтому ничего не выбрано.

Итак, решение состоит в том, чтобы преобразовать выбранное вами значение в строку в ngOnInit, и она будет работать.

Хуан
источник
1
Привет, Хуан, вы можете посмотреть этот пост, в котором подробно рассказывается о различных операторах равенства в JS: stackoverflow.com/questions/359494/…
Уильям Мур,
Привет, Уильям, это отличный пост, я был там несколько раз ... И я научился правильно сравнивать (я надеюсь, и я всегда могу просмотреть документ) ... Проблема здесь в том, что привязки, вызванные контроллер материала, где используются разные типы, числа и строки ... Этот контроллер ожидает, что он будет иметь одинаковые типы, поэтому, если выбрано число, коллекция должна быть набором чисел ... В этом была проблема.
Хуан,
0

Я сделал это.

<div>
    <mat-select [(ngModel)]="selected">
        <mat-option *ngFor="let option of options" 
            [value]="option.id === selected.id ? selected : option">
            {{ option.name }}
        </mat-option>
    </mat-select>
</div>

Обычно вы можете это сделать [value]="option", если только вы не получите свои варианты из какой-либо базы данных ?? Я думаю, что либо задержка получения данных приводит к тому, что он не работает, либо полученные объекты каким-то образом отличаются, даже если они одинаковы? Как ни странно, скорее всего, это более поздний вариант, я тоже пробовал, [value]="option === selected ? selected : option"но не сработал.

Новичек
источник
0

TS

   optionsFG: FormGroup;
   this.optionsFG = this.fb.group({
       optionValue: [null, Validators.required]
   });

   this.optionsFG.get('optionValue').setValue(option[0]); //option is the arrayName

HTML

   <div class="text-right" [formGroup]="optionsFG">
     <mat-form-field>
         <mat-select placeholder="Category" formControlName="optionValue">
           <mat-option *ngFor="let option of options;let i =index" [value]="option">
            {{option.Value}}
          </mat-option>
        </mat-select>
      </mat-form-field>
  </div>
Арокия Лиджас
источник
0

public options2 = [
  {"id": 1, "name": "a"},
  {"id": 2, "name": "b"}
]
 
YourFormGroup = FormGroup; 
mode: 'create' | 'update' = 'create';

constructor(@Inject(MAT_DIALOG_DATA) private defaults: defautValuesCpnt,
      private fb: FormBuilder,
      private cd: ChangeDetectorRef) {
}
  
ngOnInit() {

  if (this.defaults) {
    this.mode = 'update';
  } else {
    this.defaults = {} as Cpnt;
  }

  this.YourFormGroup.patchValue({
    ...
    fCtrlName: this.options2.find(x => x.name === this.defaults.name).id,
    ... 
  });

  this.YourFormGroup = this.fb.group({
    fCtrlName: [ , Validators.required]
  });

}
  <div>
    <mat-select formControlName="fCtrlName"> <mat-option
          *ngFor="let option of options2"
          value="{{ option.id }}">
        {{ option.name }}
      </mat-option>
    </mat-select>
  </div>

Лиландос Дидас
источник
Это поможет, когда вы используете Edit & Update в безопасном компоненте,
lilandos didas