Materiał kątowy: mat-select nie wybiera domyślnego


109

Mam mat-select, w którym opcje są wszystkimi obiektami zdefiniowanymi w tablicy. Próbuję ustawić wartość domyślną na jedną z opcji, jednak pozostaje ona zaznaczona podczas renderowania strony.

Mój plik maszynopisu zawiera:

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

Mój plik HTML zawiera:

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

Próbowałem ustawienie selected2i tym valuesię mat-optionzarówno do obiektu i to id, i spróbował wykorzystać zarówno [(value)]i [(ngModel)]w mat-select, ale żaden z nich nie działa.

Używam materiału w wersji 2.0.0-beta.10


2
Użyj compareWith. Jest bardziej elegancki.
— Badis Merabet

MUSI MIEĆ compareWith, zobacz odpowiedź badis tutaj stackoverflow.com/questions/47333171/…
— bresleveloper

Odpowiedzi:


144

Użyj powiązania dla wartości w szablonie.

value="{{ option.id }}"

Powinien być

[value]="option.id"

W wybranej wartości użyj ngModelzamiast value.

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

Powinien być

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

Kompletny kod:

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

Na marginesie od wersji 2.0.0-beta.12 wybór materiału akceptuje teraz mat-form-fieldelement jako element nadrzędny, dzięki czemu jest spójny z innymi kontrolkami wprowadzania materiału. Po aktualizacji zamień divelement na mat-form-fieldelement.

<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
„Wygląda na to, że używasz ngModel w tym samym polu formularza co formControlName. Obsługa używania właściwości wejściowej ngModel i zdarzenia ngModelChange z dyrektywami formularza reaktywnego została wycofana w Angular v6 i zostanie usunięta w Angular v7. Więcej informacji na ten temat zobacz naszą dokumentację API tutaj: angular.io/api/forms/FormControlName#use-with-ngmodel "
— ldgorman

1
@ldgorman - nie rozumiem, jak wyciągasz takie wnioski. Jeśli masz na myśli mat-form-field, to jest to ..."used to wrap several Angular Material components and apply common Text field styles", więc nie to samo. Poza tym, że PO i również moja odpowiedź nie wspomniał FormControl, FormGrouplub FormControlName.
— Igor

1
Mam ten sam problem nawet po wdrożeniu tego samego kodu, co powyżej @Igor
— Chuck,

@Chuck - jeśli nadal masz problemy, zadaj nowe pytanie i dołącz minimalny powtarzalny przykład . Jeśli chcesz, żebym się przyjrzał, możesz odpowiedzieć na ten komentarz, podając link do tego pytania.
— Igor

2
@ Igor- Zrozumieliśmy, wartość była zwracana jako liczba i Mat-wybierz ją szukając ciągu. [compareWith]Dyrektywa jest tym, czego użyliśmy
— Chuck

94

Użyj compareWithfunkcji, aby porównać wartości opcji z wybranymi wartościami. zobacz tutaj: https://material.angular.io/components/select/api#MatSelect

W przypadku obiektu o następującej budowie:

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

Zdefiniuj znaczniki w ten sposób:

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

I zdefiniuj funkcję porównawczą w ten sposób:

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

8
Idealny, gdy mamy do czynienia z obiektami, a nie prostymi tablicami. Dziękuję Ci.
— Riaan van Zyl

25

Używam Angular 5 i formularzy reaktywnych z mat-select i nie mogę uzyskać żadnego z powyższych rozwiązań, aby wyświetlić wartość początkową.

Musiałem dodać [compareWith], aby poradzić sobie z różnymi typami używanymi w komponencie mat-select. Wewnętrznie wydaje się, że mat-select używa tablicy do przechowywania wybranej wartości. Jest to prawdopodobne, że ten sam kod będzie działał z wieloma zaznaczeniami, jeśli ten tryb jest włączony.

Angular Select Control Doc

Oto moje rozwiązanie:

Form Builder do zainicjowania kontrolki formularza:

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

Następnie zaimplementuj funkcję compareWith w swoim komponencie:

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

Następnie utwórz i wyeksportuj funkcję defineId (musiałem utworzyć samodzielną funkcję, aby mat-select mógł jej użyć):

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

Na koniec dodaj atrybut compareWith do swojego 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>

Wielkie dzięki! Bardzo trudno było znaleźć przyczynę.
— Pax Plaża

@ Heather92065 To jedyne rozwiązanie, które u mnie zadziałało. Jestem Ci bardzo wdzięczny!
— Latin Warrior

16

Należy je jako wiążące [value]w mat-optionjak poniżej,

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

DEMO NA ŻYWO


To działa doskonale. Zamiast użycia ngModel lub setValue () jest to najłatwiejsza i doskonała metoda
— Rohit Parte

10

Możesz po prostu zaimplementować własną funkcję porównania

[compareWith]="compareItems"

Zobacz także dokumentację . Tak więc cały kod wyglądałby tak:

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

aw pliku Typescript:

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

To zadziałało dla mnie i myślę, że jest to głównie poprawny sposób, ale jeśli lista zawiera tylko jeden element, to nie działa. Dzięki
— Kod Kadiya

Jaki wyjątek otrzymujesz, mając tylko jeden element? Ponieważ porównanie powinno wziąć pod uwagę, jeśli i1lub i2nie będzie.
— LeO

6

Jak już wspomniano w Angular 6, użycie ngModel w formach reaktywnych jest przestarzałe (i usunięte w Angular 7), więc zmodyfikowałem szablon i komponent w następujący sposób.

Szablon:

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

Główne części komponentu ( onChangesi inne szczegóły zostały pominięte):

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

Zwróć uwagę na this.filter.setValue (this.selected) w ngOnInitpowyższym.

Wydaje się, że działa w Angular 6.


To właściwie powinna być najlepsza odpowiedź, ponieważ obejmuje to również wybory obiektów w przypadku dwóch różnych wyników API do porównania.
— Marco Klein

(np. Całkowita lista elementów do wyboru i wybrany element w ramach innego wywołania interfejsu API).
— Marco Klein

Angular 7 nadal działa z modelami opartymi na szablonach! Ale nie można go mieszać z formami reaktywnymi na tym samym szablonie. Twoja podpowiedź [compareWith]była świetna
— LeO

3

Zrobiłem to tak, jak w tych przykładach. Podjęto próbę ustawienia wartości opcji mat-select na wartość jednej z opcji mat. Ale zawiodło.

Moim błędem było zrobienie [(value)] = "someNumberVariable" do zmiennej typu numerycznego, podczas gdy te w mat-options były łańcuchami. Nawet jeśli wyglądałyby tak samo w szablonie, nie wybrałoby tej opcji.

Po przeanalizowaniu someNumberVariable do łańcucha wszystko było w porządku.

Wygląda więc na to, że musisz mieć wartości mat-select i mat-option nie tylko te same liczby (jeśli prezentujesz liczby), ale także niech będą typu string.


To też był mój problem. Jeden był numeryczny, drugi był łańcuchem.
— Vadim Berman

2

Rozwiązaniem dla mnie było:

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


});

Korzystanie z obiektu: {id: number, detalle: string}


1

Spróbuj tego!

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

Moje rozwiązanie jest trochę skomplikowane i prostsze.

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

Właśnie użyłem symbolu zastępczego . Domyślny kolor symbolu zastępczego materiału to light gray. Aby wyglądało na to, że opcja jest wybrana, po prostu manipulowałem CSS w następujący sposób:

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

1

Powiązanie lub ustawienie wartości domyślnej działa tylko wtedy, gdy atrybut value w MatSelect jest porównywalny z atrybutem value powiązanym z MatOption . Jeśli przypiszesz captionswój przedmiot do atrybutu value elementu mat-option , musisz również ustawić domyślny element na mat-select na captionelementu. Jeśli łączysz Idswój przedmiot z opcją mat , musisz również powiązać idgo z mat-select , a nie całym przedmiotem, podpisem lub jakimkolwiek innym, tylko tym samym polem.

Ale musisz to zrobić z wiązaniem []


1

Postępowałem zgodnie z powyższym bardzo uważnie i nadal nie mogłem uzyskać wybranej początkowej wartości.

Powodem było to, że chociaż moja powiązana wartość została zdefiniowana jako ciąg znaków w skrypcie, moje zaplecze API zwracało liczbę.

Luźne pisanie w Javascript po prostu zmieniło typ w czasie wykonywania (bez błędów), co uniemożliwiło wybór wartości początkowej.

Składnik

myBoundValue: string;

Szablon

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

Rozwiązaniem była aktualizacja interfejsu API, aby zwracał wartość ciągu.


1

Bardzo prostym sposobem osiągnięcia tego jest użycie formControlwartości domyślnej, FormGroupna przykład wewnątrz (opcjonalnie). Oto przykład użycia selektora jednostek do wejścia obszaru:

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

Porównanie liczby i ciągu znaków jest fałszywe , więc rzuć wybraną wartość na ciąg w ngOnInit i zadziała.

Miałem ten sam problem, wypełniłem mat-select wyliczeniem, używając

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

i miałem wartość wyliczenia, którą chciałem wybrać ...

Spędziłem kilka godzin walcząc z umysłem, próbując ustalić, dlaczego to nie działa. I zrobiłem to zaraz po wyrenderowaniu wszystkich zmiennych używanych w mat-select, kolekcji kluczy i wybranych ... jeśli masz ["0", "1", "2"] i chcesz wybrać 1 ( która jest liczbą) 1 == „1” jest fałszem i dlatego nic nie jest wybierane.

więc rozwiązaniem jest rzutowanie wybranej wartości na ciąg w ngOnInit i to zadziała.


1
Cześć Juan, możesz spojrzeć na ten post, który zawiera szczegółowe informacje na temat różnych operatorów równości w JS: stackoverflow.com/questions/359494/…
— William Moore

Cześć William, to świetny post, byłem tam kilka razy ... I nauczyłem się poprawnie porównywać (mam nadzieję i zawsze mogę przejrzeć dokument) ... Problem polegał na tym, że wiązania wymuszone przez kontroler materiału, gdzie używa się różnych typów, liczb i ciągów ... Ten kontroler oczekuje, że będzie miał te same typy, więc jeśli wybrano liczbę, kolekcja musi być zbiorem liczb ... To był problem.
— Juan

0

Ja to zrobiłem.

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

Zwykle możesz to zrobić [value]="option", chyba że masz opcje z jakiejś bazy danych? Myślę, że opóźnienie w uzyskaniu danych powoduje, że nie działają, albo otrzymane obiekty są w jakiś sposób różne, mimo że są takie same? Co dziwne, najprawdopodobniej jest to późniejsze, ponieważ też próbowałem [value]="option === selected ? selected : option"i nie zadziałało.


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>


Pomoże to, gdy używasz edycji i aktualizacji w bezpiecznym komponencie,
— lilandos didas
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.