Material angular: mat-select sin seleccionar predeterminado

109

Tengo un mat-select donde las opciones son todos los objetos definidos en una matriz. Estoy tratando de establecer el valor predeterminado en una de las opciones, sin embargo, se deja seleccionado cuando se procesa la página.

Mi archivo mecanografiado contiene:

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

Mi archivo HTML contiene:

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

He intentado fijar selected2y valueen mat-optionque tanto el objeto como su ID, y han intentado usar tanto [(value)]y [(ngModel)]en elmat-select , pero ninguno está trabajando.

Estoy usando la versión de material 2.0.0-beta.10

William Moore
fuente
2
Utilice compareWith. Es más elegante.
Badis Merabet
DEBE TENER compareWith, vea la respuesta de badis aquí stackoverflow.com/questions/47333171/…
bresleveloper

Respuestas:

144

Utilice un enlace para el valor en su plantilla.

value="{{ option.id }}"

debiera ser

[value]="option.id"

Y en su valor seleccionado use en ngModellugar de value.

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

debiera ser

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

Código completo:

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

En una nota al margen a partir de la versión 2.0.0-beta.12, la selección de material ahora acepta un mat-form-fieldelemento como elemento principal, por lo que es coherente con los otros controles de entrada de material. Reemplace el divelemento con el mat-form-fieldelemento después de actualizar.

<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>
Igor
fuente
10
"Parece que estás usando ngModel en el mismo campo de formulario que formControlName. El soporte para usar la propiedad de entrada ngModel y el evento ngModelChange con directivas de formulario reactivo ha quedado obsoleto en Angular v6 y se eliminará en Angular v7. Para obtener más información sobre esto , consulte nuestros documentos de API aquí: angular.io/api/forms/FormControlName#use-with-ngmodel "
ldgorman
1
@ldgorman - No veo cómo está sacando esa conclusión. Si te refieres a mat-form-fieldesto ..."used to wrap several Angular Material components and apply common Text field styles", no es lo mismo. Aparte de que el OP y también mi respuesta no hizo mención FormControl, FormGroupo FormControlName.
Igor
1
Tengo el mismo problema incluso después de implementar el mismo código que el anterior @Igor
Chuck
@Chuck: si aún tiene problemas, haga una nueva pregunta e incluya un ejemplo mínimo reproducible . Si quieres que eche un vistazo, puedes responder a este comentario con un enlace a esa pregunta.
Igor
2
@ Igor: lo descubrimos, el valor se devolvía como un número y el Mat lo seleccionaba buscando una cadena. [compareWith]directiva es lo que usamos
Chuck
94

Utilice compareWith, una función para comparar los valores de las opciones con los valores seleccionados. ver aquí: https://material.angular.io/components/select/api#MatSelect

Para un objeto de la siguiente estructura:

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

Defina el marcado como este:

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

Y defina la función de comparación como esta:

compareObjects(o1: any, o2: any): boolean {
  return o1.name === o2.name && o1.id === o2.id;
}
Badis Merabet
fuente
8
Perfecto cuando se trata de objetos y no simples matrices. Gracias.
Riaan van Zyl
25

Estoy usando Angular 5 y formas reactivas con mat-select y no pude obtener ninguna de las soluciones anteriores para mostrar el valor inicial.

Tuve que agregar [compareWith] para lidiar con los diferentes tipos que se utilizan dentro del componente mat-select. Internamente, parece que mat-select usa una matriz para contener el valor seleccionado. Es probable que esto permita que el mismo código funcione con múltiples selecciones si ese modo está activado.

Documento de control de selección angular

Esta es mi solución:

Form Builder para inicializar el control de formulario:

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

Luego implemente la función compareWith en su componente:

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

A continuación, cree y exporte la función determineId (tuve que crear una función independiente para que mat-select pudiera usarla):

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

Finalmente agregue el atributo compareWith a su 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
fuente
¡Muchas gracias! Fue muy difícil encontrar una causa.
Pax Beach
@ Heather92065 Esta es la única solución que funcionó para mí. ¡Estoy muy agradecido contigo!
Latin Warrior
16

Usted debe ser vinculante como [value]en el mat-optionde la siguiente manera,

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

DEMO EN VIVO

Aravind
fuente
Esto funciona perfectamente. En lugar de usar ngModel o setValue (), este es el método más fácil y perfecto
Rohit Parte
10

Simplemente puede implementar su propia función de comparación

[compareWith]="compareItems"

Vea también el docu . Entonces el código completo se vería así:

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

y en el archivo Typecript:

  compareItems(i1, i2) {
    return i1 && i2 && i1.id===i2.id;
  }
León
fuente
Esto funcionó para mí y creo que es en su mayoría la forma correcta, pero si la lista contiene solo un elemento, no está funcionando. Gracias
Code Kadiya
¿Qué tipo de excepción obtiene con un solo elemento? Porque la comparación debería hacerse cargo si existe i1o i2no.
LeO
6

Como ya se mencionó en Angular 6, el uso de ngModel en formas reactivas está en desuso (y se eliminó en Angular 7), por lo que modifiqué la plantilla y el componente de la siguiente manera.

La plantilla:

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

Las partes principales del componente ( onChangesy otros detalles se omiten):

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

Tenga en cuenta this.filter.setValue (this.selected) enngOnInit arriba.

Parece funcionar en Angular 6.

mp31415
fuente
En realidad, esta debería ser la mejor respuesta, ya que también cubre las selecciones de objetos cuando se trata de dos resultados API diferentes para comparar.
Marco Klein
(por ejemplo, lista total de elementos para seleccionar y elemento seleccionado dentro de otra llamada api).
Marco Klein
¡Angular 7 todavía funciona con modelos basados ​​en plantillas! Pero no se puede mezclar con formas reactivas en la misma plantilla. Tu pista con el [compareWith]fue genial
LeO
3

Lo hice como en estos ejemplos. Intenté establecer el valor de mat-select en el valor de una de las mat-options. Pero falló.

Mi error fue hacer [(value)] = "someNumberVariable" a una variable de tipo numérico mientras que las de mat-options eran cadenas. Incluso si tuvieran el mismo aspecto en la plantilla, no seleccionaría esa opción.

Una vez que analicé someNumberVariable en una cadena, todo estuvo totalmente bien.

Por lo tanto, parece que necesita que los valores de mat-select y mat-option no solo sean el mismo número (si está presentando números) sino que también deben ser de tipo cadena.

jg80
fuente
Ese también era mi problema. Uno era numérico, el otro era una cadena.
Vadim Berman
2

La solución para mí fue:

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


});

Usando objeto: {id: número, detalle: cadena}

Seba Arce
fuente
1

¡Prueba esto!

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
Facu Quintana
fuente
1

Mi solución es un poco complicada y más sencilla.

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

Acabo de hacer uso del marcador de posición . El color predeterminado del marcador de posición de material es light gray. Para que parezca que la opción está seleccionada, simplemente manipulé el CSS de la siguiente manera:

::ng-deep .mat-select-placeholder {
    color: black;
}
Steffi Keran Rani J
fuente
1

La vinculación o configuración del valor predeterminado funciona solo si el atributo de valor en MatSelect es comparable al atributo de valor vinculado a MatOption . Si vincula captionsu artículo al atributo de valor del elemento mat-option , también debe establecer el elemento predeterminado en mat-select to captionde su artículo. Si enlaza Idsu artículo a mat-option , debe enlazar ida mat-select , no a un artículo completo, título o cualquier otro, solo el mismo campo.

Pero debes hacerlo con enlace []

Dominik Miedziński
fuente
1

Seguí lo anterior con mucho cuidado y todavía no pude seleccionar el valor inicial.

La razón fue que, aunque mi valor vinculado se definió como una cadena en mecanografiado, mi API de backend estaba devolviendo un número.

La escritura suelta de Javascript simplemente cambió el tipo en tiempo de ejecución (sin error), lo que impidió la selección del valor inicial.

Componente

myBoundValue: string;

Modelo

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

La solución fue actualizar la API para devolver un valor de cadena.

Alex Cooper
fuente
1

Una forma muy sencilla de lograr esto es usar un formControlvalor predeterminado, dentro de unFormGroup (opcional) por ejemplo. Este es un ejemplo que usa un selector de unidad para una entrada de área:

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>
Juan antonio
fuente
0

Una comparación entre un número y una cadena suele ser falsa suele , por lo tanto, transfiera el valor seleccionado a una cadena dentro de ngOnInit y funcionará.

Tuve el mismo problema, llené el mat-select con una enumeración, usando

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

y tenía el valor de enumeración que quería seleccionar ...

Pasé algunas horas luchando con mi mente tratando de identificar por qué no estaba funcionando. Y lo hice justo después de renderizar todas las variables que se utilizan en mat-select, la colección de claves y el seleccionado ... si tiene ["0", "1", "2"] y desea seleccionar 1 ( que es un número) 1 == "1" es falso y por eso no se selecciona nada.

Entonces , la solución es convertir el valor seleccionado en una cadena dentro de ngOnInit y funcionará.

Juan
fuente
1
Hola Juan, es posible que desee ver esta publicación que detalla los diferentes operadores de igualdad en JS: stackoverflow.com/questions/359494/…
William Moore
Hola William, esa es una gran publicación, he estado allí pocas veces ... Y aprendí a comparar correctamente (espero, y siempre puedo revisar el documento) ... El problema aquí fue que las fijaciones, forzadas por el controlador de material, donde se utilizan diferentes tipos, números y cadenas ... Ese controlador espera tener los mismos tipos, así que, si se selecciona un número, la colección debe ser una colección de números ... Ese era el problema.
Juan
0

Hice esto.

<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 puede hacerlo [value]="option", a menos que obtenga sus opciones de alguna base de datos. Creo que la demora en obtener los datos hace que no funcionen, o los objetos obtenidos son diferentes de alguna manera a pesar de que son iguales. Curiosamente, es muy probable que sea el último, ya que también lo intenté [value]="option === selected ? selected : option"y no funcionó.

Novato
fuente
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>
Arokia Lijas
fuente
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
fuente
Esto le ayudará cuando utilice Editar y actualizar en el componente seguro,
lilandos didas