Matériau angulaire: sélection du tapis sans sélection par défaut


109

J'ai un mat-select où les options sont tous les objets définis dans un tableau. J'essaie de définir la valeur par défaut sur l'une des options, mais elle est laissée sélectionnée lorsque la page s'affiche.

Mon fichier dactylographié contient:

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

Mon fichier HTML contient:

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

J'ai essayé de placer selected2et valuedans mat-optionà la fois l'objet et son identifiant, et ont essayé d' utiliser à la fois [(value)]et [(ngModel)]dans le mat-select, mais aucun ne fonctionne.

J'utilise la version matérielle 2.0.0-beta.10


2
Utilisez compareWith. C'est plus élégant.
Badis Merabet

DOIT AVOIR compareWith, voir la réponse de badis ici stackoverflow.com/questions/47333171/…
bresleveloper

Réponses:


144

Utilisez une liaison pour la valeur de votre modèle.

value="{{ option.id }}"

devrait être

[value]="option.id"

Et dans votre utilisation de valeur sélectionnée ngModelau lieu de value.

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

devrait être

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

Code complet:

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

Par ailleurs, à partir de la version 2.0.0-beta.12, la sélection de matériaux accepte désormais un mat-form-fieldélément comme élément parent afin qu'il soit cohérent avec les autres contrôles d'entrée de matériaux. Remplacez l' divélément par l' mat-form-fieldélément après la mise à niveau.

<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
"Il semble que vous utilisez ngModel sur le même champ de formulaire que formControlName. La prise en charge de l'utilisation de la propriété d'entrée ngModel et de l'événement ngModelChange avec des directives de formulaire réactives a été déconseillée dans Angular v6 et sera supprimée dans Angular v7. Pour plus d'informations à ce sujet , consultez nos documents sur l'API ici: angular.io/api/forms/FormControlName#use-with-ngmodel "
ldgorman

1
@ldgorman - Je ne vois pas comment vous tirez cette conclusion. Si vous faites référence à mat-form-field, ce n'est ..."used to wrap several Angular Material components and apply common Text field styles"donc pas la même chose. Autre que l'OP et aussi ma réponse ne fait aucune mention FormControl, FormGroupou FormControlName.
Igor

1
J'ai le même problème même après avoir implémenté le même code que ci-dessus @Igor
Chuck

@Chuck - Si vous rencontrez toujours des problèmes, posez une nouvelle question et incluez un exemple reproductible minimal . Si vous voulez que je regarde, vous pouvez répondre à ce commentaire avec un lien vers cette question.
Igor

2
@ Igor- Nous l'avons compris, la valeur était renvoyée sous forme de nombre et le Mat-sélectionnez-le à la recherche d'une chaîne. [compareWith]directive est ce que nous avons utilisé
Chuck

94

Utilisez compareWith, Une fonction pour comparer les valeurs d'option avec les valeurs sélectionnées. voir ici: https://material.angular.io/components/select/api#MatSelect

Pour un objet de la structure suivante:

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

Définissez le balisage comme ceci:

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

Et définissez la fonction de comparaison comme ceci:

compareObjects(o1: any, o2: any): boolean {
  return o1.name === o2.name && o1.id === o2.id;
}

8
Parfait lorsqu'il s'agit d'objets et non de tableaux simples. Je vous remercie.
Riaan van Zyl

25

J'utilise Angular 5 et des formulaires réactifs avec mat-select et je n'ai pas pu obtenir l'une des solutions ci-dessus pour afficher la valeur initiale.

J'ai dû ajouter [compareWith] pour gérer les différents types utilisés dans le composant mat-select. En interne, il semble que mat-select utilise un tableau pour contenir la valeur sélectionnée. Cela permettra probablement au même code de fonctionner avec plusieurs sélections si ce mode est activé.

Doc de contrôle de sélection angulaire

Voici ma solution:

Form Builder pour initialiser le contrôle de formulaire:

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

Ensuite, implémentez la fonction compareWith sur votre composant:

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

Ensuite, créez et exportez la fonction determineId (j'ai dû créer une fonction autonome pour que mat-select puisse l'utiliser):

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

Enfin, ajoutez l'attribut compareWith à votre sélection de tapis:

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

Merci beaucoup! Il était très difficile de trouver une cause.
Pax Beach

@ Heather92065 C'est la seule solution qui a fonctionné pour moi. Je te suis vraiment reconnaissant!
Latin Warrior

16

Vous devez le lier comme [value]dans le mat-optioncomme ci - dessous,

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

DÉMO EN DIRECT


Cela fonctionne parfaitement. Insted d'utiliser ngModel ou setValue () c'est la méthode la plus simple et parfaite
Rohit Parte

10

Vous pouvez simplement implémenter votre propre fonction de comparaison

[compareWith]="compareItems"

Voir aussi le docu . Le code complet ressemblerait donc à:

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

et dans le fichier Typescript:

  compareItems(i1, i2) {
    return i1 && i2 && i1.id===i2.id;
  }

Cela fonctionne pour moi et je pense que c'est la manière la plus correcte, mais si la liste ne contient qu'un seul élément, cela ne fonctionne pas. Merci
Code Kadiya

Quel genre d'exception obtenez-vous avec un seul élément? Parce que la comparaison devrait prendre en charge si oui i1ou i2non.
LeO du

6

Comme déjà mentionné dans Angular 6, l'utilisation de ngModel sous des formes réactives est obsolète (et supprimée dans Angular 7), j'ai donc modifié le modèle et le composant comme suit.

Le gabarit:

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

Les parties principales du composant ( onChangeset les autres détails sont omis):

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

Remarque this.filter.setValue (this.selected) au - ngOnInitdessus.

Cela semble fonctionner dans Angular 6.


Cela devrait en fait être la meilleure réponse, car elle couvre également les sélections d'objets lorsqu'il s'agit de deux résultats API différents à comparer.
Marco Klein du

(par exemple, liste totale des éléments à sélectionner et élément sélectionné dans un autre appel API).
Marco Klein du

Angular 7 fonctionne toujours avec des modèles pilotés basés sur des modèles! Mais vous ne pouvez pas le mélanger avec des formulaires réactifs sur le même modèle. Votre indice avec le [compareWith]was great
LeO

3

Je l'ai fait comme dans ces exemples. J'ai essayé de définir la valeur de la sélection de tapis sur la valeur de l'une des options de tapis. Mais a échoué.

Mon erreur a été de faire [(value)] = "someNumberVariable" à une variable de type numérique alors que celles de mat-options étaient des chaînes. Même s'ils avaient la même apparence dans le modèle, cette option ne serait pas sélectionnée.

Une fois que j'ai analysé le someNumberVariable en une chaîne, tout allait parfaitement bien.

Il semble donc que vous ayez besoin que les valeurs mat-select et mat-option soient non seulement le même nombre (si vous présentez des nombres), mais aussi qu'elles soient de type string.


C'était aussi mon problème. L'un était numérique, l'autre était une chaîne.
Vadim Berman

2

La solution pour moi était:

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


});

Utilisation de l'objet: {id: number, detalle: string}


1

Essaye ça!

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

Ma solution est peu compliquée et plus simple.

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

Je viens d'utiliser l' espace réservé . La couleur par défaut de l'espace réservé de matériau est light gray. Pour donner l'impression que l'option est sélectionnée, j'ai simplement manipulé le CSS comme suit:

::ng-deep .mat-select-placeholder {
    color: black;
}

1

La liaison ou la définition de la valeur par défaut ne fonctionne que si l' attribut value sur MatSelect est comparable à l' attribut value lié à MatOption . Si vous liez captionvotre élément à l' attribut value de l' élément mat-option , vous devez également définir l'élément par défaut sur mat-select sur captionde votre élément. Si vous liez Idvotre élément à mat-option , vous devez également vous lier idà mat-select , pas à un élément entier, à une légende ou à tout autre, seulement au même champ.

Mais vous devez le faire avec la liaison []


1

J'ai suivi très attentivement ce qui précède et je n'ai toujours pas pu sélectionner la valeur initiale.

La raison en était que bien que ma valeur liée ait été définie comme une chaîne dans le typographie, mon API backend renvoyait un nombre.

Javascript lâche typage a simplement changé le type à l'exécution (sans erreur), ce qui a empêché la sélection de la valeur initiale.

Composant

myBoundValue: string;

Modèle

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

La solution était de mettre à jour l'API pour renvoyer une valeur de chaîne.


1

Un moyen très simple d'y parvenir consiste à utiliser a formControlavec une valeur par défaut, à l'intérieur de a FormGroup(facultatif) par exemple. Voici un exemple utilisant un sélecteur d'unité pour une entrée de zone:

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

Une comparaison entre un nombre et une chaîne est fausse , donc, transtypez la valeur sélectionnée en une chaîne dans ngOnInit et cela fonctionnera.

J'ai eu le même problème, j'ai rempli le mat-select avec une énumération, en utilisant

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

et j'avais la valeur enum que je voulais sélectionner ...

J'ai passé quelques heures à essayer de comprendre pourquoi cela ne fonctionnait pas. Et je l'ai fait juste après avoir rendu toutes les variables utilisées dans le mat-select, la collection de clés et la sélection ... si vous avez ["0", "1", "2"] et que vous voulez sélectionner 1 ( qui est un nombre) 1 == "1" est faux et à cause de cela rien n'est sélectionné.

donc, la solution consiste à convertir la valeur sélectionnée en une chaîne dans ngOnInit et cela fonctionnera.


1
Salut Juan, vous voudrez peut-être consulter cet article qui rentre dans les détails sur les différents opérateurs d'égalité dans JS: stackoverflow.com/questions/359494/…
William Moore

Salut William, c'est un super post, j'y suis allé plusieurs fois ... Et j'ai appris à bien comparer (j'espère, et je pourrai toujours revoir la doc) ... Le problème ici était que les fixations, forcées par le contrôleur de matériaux, où l'utilisation de différents types, nombres et chaînes ... Ce contrôleur s'attend à avoir les mêmes types, donc, si sélectionné est un nombre, la collection doit être une collection de nombres ... C'était le problème.
Juan

0

J'ai fait ça.

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

Normalement, vous pouvez le faire [value]="option", à moins que vous n'obteniez vos options à partir d'une base de données ?? Je pense que soit le retard d'obtention des données les empêche de fonctionner, soit les objets obtenus sont différents d'une certaine manière même s'ils sont les mêmes ?? Curieusement, c'est probablement le plus récent, car j'ai également essayé [value]="option === selected ? selected : option"et cela n'a pas fonctionné.


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>


Cela vous aidera lorsque vous utilisez Edit & Update dans le composant sécurisé,
lilandos didas
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.