Materiale angolare: selezione del tappetino che non seleziona l'impostazione predefinita


109

Ho un mat-select dove le opzioni sono tutti gli oggetti definiti in un array. Sto cercando di impostare il valore predefinito su una delle opzioni, tuttavia viene lasciato selezionato quando la pagina viene visualizzata.

Il mio file dattiloscritto contiene:

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

Il mio file HTML contiene:

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

Ho provato a installare selected2e valuein mat-optionsia per l'oggetto e di identificazione, e ho provato utilizzando sia [(value)]e [(ngModel)]in mat-select, ma nessuno sta lavorando.

Sto usando la versione materiale 2.0.0-beta.10


2
Usa compareWith. È più elegante.
Badis Merabet

DEVE AVERE compareWith, vedere badis risposta qui stackoverflow.com/questions/47333171/...~~V~~plural~~3rd
bresleveloper

Risposte:


144

Usa un'associazione per il valore nel tuo modello.

value="{{ option.id }}"

dovrebbe essere

[value]="option.id"

E nel valore selezionato usa ngModelinvece di value.

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

dovrebbe essere

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

Codice completo:

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

In una nota a margine della versione 2.0.0-beta.12, la selezione del materiale ora accetta un mat-form-fieldelemento come elemento padre, quindi è coerente con gli altri controlli di input del materiale. Sostituisci l' divelemento con l' mat-form-fieldelemento dopo l'aggiornamento.

<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
"Sembra che tu stia utilizzando ngModel sullo stesso campo modulo di formControlName. Il supporto per l'utilizzo della proprietà di input ngModel e dell'evento ngModelChange con le direttive del modulo reattivo è stato deprecato in Angular v6 e verrà rimosso in Angular v7. Per ulteriori informazioni su questo , consulta i nostri documenti API qui: angular.io/api/forms/FormControlName#use-with-ngmodel "
ldgorman

1
@ldgorman - Non vedo come tragga questa conclusione. Se ti riferisci a mat-form-field, questo è ..."used to wrap several Angular Material components and apply common Text field styles", quindi non è la stessa cosa. Diverso da quello che il PO e anche la mia risposta non ha fatto menzione FormControl, FormGroupo FormControlName.
Igor

1
Ho lo stesso problema anche dopo aver implementato lo stesso codice di cui sopra @Igor
Chuck

@ Chuck - Se hai ancora problemi, fai una nuova domanda e includi un esempio minimo riproducibile . Se vuoi che dia un'occhiata puoi rispondere a questo commento con un link a quella domanda.
Igor

2
@ Igor- Lo abbiamo capito, il valore veniva restituito come numero e il Mat lo selezionava cercando una stringa. [compareWith]la direttiva è ciò che abbiamo usato
Chuck

94

Utilizzare compareWith, una funzione per confrontare i valori delle opzioni con i valori selezionati. vedere qui: https://material.angular.io/components/select/api#MatSelect

Per un oggetto della seguente struttura:

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

Definisci il markup in questo modo:

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

E definisci la funzione di confronto in questo modo:

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

8
Perfetto quando si ha a che fare con oggetti e non semplici array. Grazie.
Riaan van Zyl

25

Sto usando Angular 5 e forme reattive con mat-select e non sono riuscito a ottenere nessuna delle soluzioni sopra per visualizzare il valore iniziale.

Ho dovuto aggiungere [compareWith] per gestire i diversi tipi utilizzati all'interno del componente mat-select. Internamente, sembra che mat-select utilizzi un array per contenere il valore selezionato. È probabile che ciò consenta allo stesso codice di funzionare con più selezioni se tale modalità è attivata.

Angular Select Control Doc

Ecco la mia soluzione:

Form Builder per inizializzare il controllo del modulo:

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

Quindi implementa la funzione compareWith sul tuo componente:

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

Quindi crea ed esporta la funzione detectId (ho dovuto creare una funzione standalone in modo che mat-select potesse usarla):

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

Infine aggiungi l'attributo compareWith alla tua selezione mat:

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

Molte grazie! È stato molto difficile trovare la causa.
Pax Beach

@ Heather92065 Questa è l'unica soluzione che ha funzionato per me. Ti sono molto grato!
Latin Warrior

16

Dovresti vincolarlo come [value]di mat-optionseguito,

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

DIMOSTRAZIONE DAL VIVO


Funziona perfettamente. Invece di usare ngModel o setValue (), questo è il metodo più semplice e perfetto
Rohit Parte

10

Puoi semplicemente implementare la tua funzione di confronto

[compareWith]="compareItems"

Vedi anche il docu . Quindi il codice completo sarebbe simile a:

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

e nel file Typescript:

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

Questo ha funzionato per me e penso che questo sia il modo più corretto ma se l'elenco contiene solo un elemento non funziona. Grazie
Code Kadiya

Che tipo di eccezione ottieni con un solo elemento? Perché il confronto dovrebbe prendere in considerazione se i1o i2non esisterà.
LeO

6

Come già accennato in Angular 6, l'utilizzo di ngModel in forme reattive è deprecato (e rimosso in Angular 7), quindi ho modificato il modello e il componente come segue.

Il template:

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

Le parti principali del componente ( onChangese altri dettagli sono omessi):

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

Nota this.filter.setValue (this.selected) in ngOnInitprecedenza.

Sembra funzionare in Angular 6.


Questa dovrebbe effettivamente essere la risposta migliore, poiché copre anche le selezioni di oggetti quando si tratta di due diversi risultati API da confrontare.
Marco Klein

(ad esempio, elenco totale di elementi da cui selezionare e elemento selezionato all'interno di un'altra chiamata API).
Marco Klein

Angular 7 funziona ancora con modelli basati su modelli! Ma non puoi mescolarlo con forme reattive sullo stesso modello. Il tuo suggerimento con il è [compareWith]stato fantastico
LeO

3

L'ho fatto proprio come in questi esempi. Ho provato a impostare il valore di mat-select sul valore di una delle opzioni mat. Ma fallito.

Il mio errore è stato quello di eseguire [(value)] = "someNumberVariable" su una variabile di tipo numerico mentre quelle in mat-options erano stringhe. Anche se avessero lo stesso aspetto nel modello, non selezionerebbe quell'opzione.

Una volta che ho analizzato someNumberVariable in una stringa, è andato tutto bene.

Quindi sembra che tu debba avere i valori mat-select e mat-option non solo essere lo stesso numero (se stai presentando numeri) ma anche lasciare che siano di tipo stringa.


Questo era anche il mio problema. Uno era numerico, l'altro era una stringa.
Vadim Berman

2

La soluzione per me era:

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


});

Utilizzo dell'oggetto: {id: number, detalle: string}


1

Prova questo!

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

La mia soluzione è un po 'complicata e più semplice.

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

Ho appena utilizzato il segnaposto . Il colore predefinito del segnaposto materiale è light gray. Per far sembrare che l'opzione sia selezionata, ho semplicemente manipolato il CSS come segue:

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

1

Il binding o l'impostazione del valore predefinito funziona solo se l' attributo value su MatSelect è paragonabile all'attributo value associato a MatOption . Se leghi il captiontuo articolo all'attributo value dell'elemento mat-option , devi impostare anche l'elemento predefinito su mat-select to captiondel tuo articolo. Se leghi il Idtuo articolo all'opzione mat , devi associare anche idalla selezione mat , non un intero articolo, didascalia o qualsiasi altro, solo lo stesso campo.

Ma devi farlo con l'associazione []


1

Ho seguito molto attentamente quanto sopra e ancora non sono riuscito a ottenere il valore iniziale selezionato.

Il motivo era che, sebbene il mio valore associato fosse definito come una stringa in dattiloscritto, la mia API di backend restituiva un numero.

La digitazione errata di Javascript ha semplicemente cambiato il tipo in fase di esecuzione (senza errori), il che ha impedito la selezione del valore iniziale.

Componente

myBoundValue: string;

Modello

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

La soluzione era aggiornare l'API per restituire un valore stringa.


1

Un modo molto semplice per ottenere ciò è utilizzare a formControlcon un valore predefinito, FormGroupad esempio all'interno di a (opzionale). Questo è un esempio che utilizza un selettore di unità per un ingresso di area:

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

Un confronto tra un numero e una stringa è falso , quindi, esegui il cast del valore selezionato su una stringa all'interno di ngOnInit e funzionerà.

Ho avuto lo stesso problema, ho riempito il mat-select con un enum, usando

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

e avevo il valore enum che volevo selezionare ...

Ho passato alcune ore a lottare con la mente cercando di identificare il motivo per cui non funzionava. E l'ho fatto subito dopo aver reso tutte le variabili utilizzate nel mat-select, la raccolta delle chiavi e il selezionato ... se hai ["0", "1", "2"] e vuoi selezionare 1 ( che è un numero) 1 == "1" è falso e per questo motivo non viene selezionato nulla.

quindi, la soluzione è eseguire il cast del valore selezionato su una stringa all'interno di ngOnInit e funzionerà.


1
Ciao Juan potresti voler guardare questo post che entra nei dettagli sui diversi operatori di uguaglianza in JS: stackoverflow.com/questions/359494/…
William Moore

Ciao William, è un ottimo post, ci sono stato poche volte ... E ho imparato a confrontare correttamente (spero, e posso sempre rivedere il documento) ... Il problema qui era che le associazioni, forzate da il controller del materiale, dove si utilizzano tipi, numeri e stringhe diversi ... Quel controller si aspetta di avere gli stessi tipi, quindi, se selezionato è un numero, la raccolta deve essere una raccolta di numeri ... Questo era il problema.
Juan

0

L'ho fatto.

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

Normalmente puoi farlo [value]="option", a meno che tu non ottenga le tue opzioni da qualche database ?? Penso che o il ritardo nell'ottenere i dati faccia sì che non funzionino, o che gli oggetti ottenuti siano diversi in qualche modo anche se sono gli stessi ?? Stranamente, molto probabilmente è l'ultimo, poiché ho anche provato [value]="option === selected ? selected : option"e non ha funzionato.


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>


Questo ti aiuterà quando utilizzi Modifica e aggiorna nel componente sicuro,
lilandos didas
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.