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.