Angular Material:mat-selectがデフォルトを選択しない


109

オプションがすべて配列で定義されたオブジェクトであるマット選択があります。値をデフォルトのオプションの1つに設定しようとしていますが、ページがレンダリングされるときに値が選択されたままになります。

私のtypescriptファイルには以下が含まれます:

  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>

オブジェクトとIDの両方に設定selected2valuein mat-optionを試し、両方[(value)][(ngModel)]で使用しようとしましたmat-selectが、どれも機能していません。

マテリアルバージョン2.0.0-beta.10を使用しています


2
を使用しcompareWithます。よりエレガントです。
Badis Merabet 2018

マストハブ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
「formControlNameと同じフォームフィールドでngModelを使用しているようです。リアクティブフォームディレクティブでの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"なので、同じではありません。OPも私の答えは何も言及していないこと以外はFormControlFormGroup、またはFormControlName
イゴール

1
上記と同じコードを実装した後でも同じ問題が発生します@Igor
Chuck

@チャック-それでも問題が解決しない場合は、新しい質問をして、最小限の再現可能な例を含めてください。あなたが私に見てもらいたいならば、あなたはその質問へのリンクでこのコメントに返信することができます。
Igor

2
@ Igor-わかりました。値は数値として返され、Mat-selectは文字列を探していました。[compareWith]ディレクティブは私たちが使用したものです
Chuck

94

compareWithオプション値を選択した値と比較するには、A関数を使用します。こちらをご覧ください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とmat-selectの反応フォームを使用していますが、上記のいずれのソリューションでも初期値を表示できませんでした。

マット選択コンポーネント内で使用されているさまざまなタイプを処理するには、[compareWith]を追加する必要がありました。内部的には、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;
}

次に、determineId関数を作成してエクスポートします(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>

どうもありがとう!原因を見つけることは非常に困難でした。
パックスビーチ

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

ライブデモ


これは完全に機能します。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;
  }

これは私にとってはうまくいき、私はこれはほとんど正しい方法だと思いますが、リストに要素が1つしか含まれていない場合は機能しません。ありがとう
コードカディヤ

1つの要素だけでどのような例外が発生しますか?存在しない場合は、比較が担当する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で動作するようです。


これは、比較する2つの異なるAPI結果を処理するときのオブジェクト選択もカバーしているため、実際にはこれが最良の答えになるはずです。
マルコクライン

(たとえば、別のAPI呼び出し内で選択する項目と選択した項目の合計リスト)。
マルコクライン

Angular 7はテンプレート化された駆動モデルでも動作します!ただし、同じテンプレート上で反応型と混在させることはできません。あなたへのヒント[compareWith]は素晴らしかった
LeO 2018

3

私はこれらの例のようにそれをしました。mat-selectの値をいずれかのmat-optionsの値に設定しようとしました。しかし失敗した。

私の間違いは、数値型変数に対して[(value)] = "someNumberVariable"を実行することでしたが、mat-optionsの変数は文字列でした。テンプレートで同じに見えても、そのオプションは選択されません。

someNumberVariableを文字列に解析すると、すべてが完全に問題なく完了しました。

したがって、mat-selectとmat-optionの値を同じ数にする必要がある(数値を表示している場合)だけでなく、文字列型にする必要があるようです。


それも私の問題でした。1つは数値で、もう1つは文字列でした。
Vadim Berman

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:数値、詳細:文字列}


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

デフォルト値のバインドまたは設定は、MatSelectvalue属性がMatOptionにバインドされたvalue属性に相当する場合のみ機能します。captionアイテムをmat-option要素のvalue属性にバインドする場合、アイテムのmat-select to のデフォルト要素も設定する必要があります。アイテムをmat-optionにバインドする場合は、アイテム全体やキャプションなどではなく、同じフィールドのみをmat-selectにもバインドする必要があります。captionIdid

しかし、バインディングでそれを行う必要があります[]


1

上記に注意深く従いましたが、初期値を選択することができませんでした。

その理由は、バインドされた値がtypescriptの文字列として定義されていても、バックエンドAPIが数値を返していたためです。

Javascriptのルーズタイピングは、実行時にタイプを(エラーなしで)変更するだけで、初期値の選択を妨げていました。

成分

myBoundValue: string;

テンプレート

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

解決策は、文字列値を返すようにAPIを更新することでした。


1

これを実現する非常に簡単な方法は、たとえば(オプション)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

数値と文字列の比較ではfalseを使用するため、選択した値をngOnInit内の文字列にキャストすると機能します。

私は同じ問題を抱えていました、マットセレクトを列挙型で埋め、

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

選択したい列挙値がありました...

私はそれがうまくいかなかった理由を特定するために頭を悩ませて数時間過ごしました。そして、mat-select、keysコレクション、およびselectedで使用されているすべての変数をレンダリングした直後にそれを行いました。["0"、 "1"、 "2"]があり、1を選択したい場合(これは数値です)1 == "1"は偽であり、そのため何も選択されていません。

したがって、解決策選択した値を ngOnInit内の文字列キャストすることであり、それは機能します。


1
こんにちはフアンJSのさまざまな等値演算子についての詳細を説明するこの投稿をご覧になるとよいでしょう。stackoverflow.com
William Moore

こんにちはウィリアム、これは素晴らしい投稿です。私は何度か行ったことがあります...そして、適切に比較する方法を学びました(私は願っていますし、常にドキュメントを確認できます)...ここでの問題は、さまざまなタイプ、数、文字列を使用するマテリアルコントローラ...そのコントローラは同じタイプであることが想定されているため、選択したものが数値の場合、コレクションは数値のコレクションでなければなりません...それが問題でした。
フアン:

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>


これは、safeコンポーネントでEdit&Updateを使用している場合に役立ちます
lilandos didas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.