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

William Moore
źródło
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>
Igor
źródło
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;
}
Badis Merabet
źródło
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>
Heather92065
źródło
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

Aravind
źródło
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;
  }
Lew
źródło
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.

mp31415
źródło
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.

jg80
źródło
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}

Seba Arce
źródło
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
Facu Quintana
źródło
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;
}
Steffi Keran Rani J
źródło
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 []

Dominik Miedziński
źródło
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.

Alex Cooper
źródło
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>
Juan Antonio
źródło
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.

Juan
źródło
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.

Nowicjusz
źródł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>
Arokia Lijas
źródło
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
źródło
Pomoże to, gdy używasz edycji i aktualizacji w bezpiecznym komponencie,
lilandos didas