Angular Material: мати-вибір не вибирає за замовчуванням


109

У мене є матовий вибір, де параметри - це всі об'єкти, визначені в масиві. Я намагаюся встановити значення за замовчуванням для одного з варіантів, однак воно залишається вибраним, коли сторінка відображається.

Мій файл машинопису містить:

  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і valueв mat-optionі об'єкт, і його id, і спробував використовувати [(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 - Якщо у вас все ще виникають проблеми, поставте нове запитання та включіть мінімальний відтворюваний приклад . Якщо ви хочете, щоб я подивився, ви можете відповісти на цей коментар за допомогою посилання на це питання.
Ігор

2
@ Igor- Ми з'ясували це, значення поверталося як число, а Mat - вибираємо його, шукаючи рядок. [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
Ідеально підходить для роботи з об'єктами, а не з простими масивами. Дякую.
Riaan van Zyl

25

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

Мені довелося додати [CompareWith], щоб розібратися з різними типами, які використовуються в компоненті mat-select. Всередині здається, що mat-select використовує масив для утримання вибраного значення. Це, ймовірно, дозволить одному і тому ж коду працювати з кількома виборами, якщо цей режим увімкнено.

Угловий вибір док

Ось моє рішення:

Формувальник форм для ініціалізації контролю форми:

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

Потім реалізуйте на своєму компоненті функцію порівняння:

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

Дуже дякую! Знайти причину було дуже важко.
Pax Beach

@ Heather92065 Це єдине рішення, яке працювало на мене. Я вам дуже вдячний!
Латинський воїн

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>

LIVE DEMO


Це прекрасно працює. Це найпростіший і досконалий метод
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;
  }

Це працює для мене, і я думаю, що це здебільшого правильний спосіб, але якщо список містить лише один елемент, він не працює. Спасибі
Код

Який виняток ви отримуєте лише з одним елементом? Тому що порівнюваний слід брати до заряд , якщо 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вище.

Здається, працює в кутовій 6.


Це насправді має бути найкращою відповіддю, оскільки це також охоплює вибір об'єктів при роботі з двома різними результатами API для порівняння.
Марко Кляйн

(наприклад, Загальний список елементів для вибору та вибраний елемент під час іншого дзвінка api).
Марко Кляйн

Angular 7 все ще працює з модельованими керованими моделями! Але ви не можете змішувати його з реакційноздатними формами на тих же шаблонах. Твій натяк на це [compareWith]був чудовий
LeO

3

Я це робив так, як у цих прикладах. Спробував встановити значення матового вибору на значення одного з варіантів мат. Але не вдалося.

Моя помилка полягала в тому, щоб зробити [(значення)] = "someNumberVariable" для числової змінної типу, тоді як ті, що знаходяться в мат-опціях, були рядками. Навіть якби вони виглядали однаково в шаблоні, він не вибрав би цей варіант.

Одного разу я розібрав деякийNumberVariable до рядка, все було абсолютно добре.

Тому, схоже, вам потрібно мати mat-select і значення mat-параметра не тільки бути однаковим числом (якщо ви представляєте числа), але і нехай вони будуть типів рядка.


Це було і моє питання. Один був числовим, інший - рядком.
Вадим Берман

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свій елемент до атрибуту значення елемента mat-option, ви також повинні встановити за замовчуванням елемент mat-select для captionвашого елемента. Якщо ви прив'язуєте Idваш елемент до мат-опції , ви також повинні прив’язувати idдо вибору мат , не весь предмет, підпис або будь-яке інше, лише те саме поле.

Але вам потрібно зробити це з прив'язкою []


1

Я дуже уважно дотримувався вищезазначеного і все ще не міг вибрати вибране початкове значення.

Причина полягала в тому, що, хоча моє обмежене значення було визначено як рядок у typecript, мій бекенд API повертав число.

Ввімкнене введення Javascript просто змінило тип під час виконання (без помилок), що перешкоджало вибору початкового значення.

Компонент

myBoundValue: string;

Шаблон

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

Рішенням було оновити API, щоб повернути значення рядка.


1

Дуже простий спосіб досягти цього - використовувати значення formControlзі значенням за замовчуванням, розташоване всерединіFormGroup (необов'язково). Це приклад використання перемикача одиниць для введення області:

ц

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 і воно буде працювати.

У мене була така ж проблема, я заповнив мат-секс із перерахуванням, використовуючи

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

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

Я кілька годин боровся з розумом, намагаючись визначити, чому це не працює. І я зробив це відразу після рендерингу всіх змінних, що використовуються в мат-виборі, колекції ключів і вибраних ... якщо у вас є ["0", "1", "2"] і ви хочете вибрати 1 ( що є числом) 1 == "1" помилково, і через це нічого не вибрано.

Таким чином, рішення полягає в тому, щоб передати вибране значення рядку в межах ngOnInit і воно буде працювати.


1
Привіт Хуан, ви можете поглянути на цю публікацію, в якій
Вільям Мур

Привіт Вільям, це чудовий пост, я був там кілька разів ... І я навчився правильно порівнювати (сподіваюся, і завжди можу переглянути док.) ... Проблема тут полягала в тому, що прив'язки, вимушені контролер матеріалів, де використовуються різні типи, число та рядки ... Цей контролер очікує мати однакові типи, тому, якщо вибрано число, колекція повинна бути сукупністю чисел ... Це було проблемою.
Хуан

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>


Це допоможе, коли ви використовуєте Правка та оновлення в безпечному компоненті,
lilandos didas
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.