0

I was able to populate dropdowns when using template driven forms but now in material reactive form i am unable to do so. I am trying to populate country dropdown so that i might be able to call a onstatechange event later on to populate states according to the selected country. Here is my code

    <div fxLayout="row wrap" class="pt-4">
        <mat-form-field appearance="outline" fxFlex="33">
            <mat-label>Select a Country</mat-label>
            <mat-select [compareWith]="compareThese" formControlName="countryCtrl" (selectionChange)="loadState($event)">
                <mat-option *ngFor="let item of countriesData$ | async" [value]="item.id">
                    {{ item.name }}
                </mat-option>
            </mat-select>
            <mat-icon matSuffix class="disabled-text">account_circle</mat-icon>
        </mat-form-field>
        <mat-form-field appearance="outline" fxFlex="34" class="px-8">
            <mat-label>Select a State</mat-label>
            <mat-select [compareWith]="compareThese" formControlName="stateCtrl" (selectionChange)="loadCity($event)">
                <mat-option *ngFor="let item of filteredStates$ | async" [value]="item.id">
                    {{ item.name }}
                </mat-option>
            </mat-select>
            <mat-icon matSuffix class="disabled-text">account_circle</mat-icon>
        </mat-form-field>
        <mat-form-field appearance="outline" fxFlex="33">
            <mat-label>Select a City</mat-label>
            <mat-select [compareWith]="compareThese" formControlName="cityCtrl">
                <mat-option *ngFor="let ele of filteredCities$ | async" [value]="ele.id">
                    {{ ele.name }}
                </mat-option>
            </mat-select>
            <mat-icon matSuffix class="disabled-text">account_circle</mat-icon>
        </mat-form-field>
    </div>

Here is how my JSON looks

enter image description here

ts file code

export class ManualFormComponent implements OnInit {

  countryCtrl: any;
  stateCtrl: any = {};
  cityCtrl: any = {};
  manualOrderForm: FormGroup;
  zoom: any;

  countriesData$: Observable<ICountry[]>;
  filteredStates$: Observable<IState[]>;
  filteredCities$: Observable<ICity[]>;

  constructor(
    private _cityStateService: CountryStateCityService,
    private _fb: FormBuilder) {
    this.manualOrderForm = this._fb.group({
      countryCtrl: [[], Validators.required], 
      stateCtrl: [[], Validators.required],
      cityCtrl: [[], Validators.required],
    });
  }

  submit() {
    console.log(this.manualOrderForm);
  }

  compareThese(o1: any, o2: any) {
    return o1 === o2 ? true : false;
  }

  ngOnInit() {
    this._cityStateService.getCountriesStatesCity().subscribe(val => {
      console.log(val);
      this.countriesData$ = val[0];
      console.log(this.countriesData$);
    });
  }

}

Interface for Country

import { IState } from './iState';

export interface ICountry {
  name: string;
  id: string;
  states: IState[];
}

State

import { ICity } from './iCity';
export interface IState {
  cities: ICity[];
  id: string;
  name: string;
  countryId: string;
}
5
  • 1
    I could be blind, but have you tried this.countriesData$ = val[0].states? Commented Dec 5, 2019 at 0:38
  • 1
    Can you update your post to include your ICountry ts please? Have you tried: countriesData$: any; instead of countriesData$: Observable<ICountry[]>;? Commented Dec 5, 2019 at 0:50
  • 1
    Perfect. Try stripping | async off of countries select and see what you get. Commented Dec 5, 2019 at 0:57
  • @Plochie can you copy paste this code inside your service and then populate the dropdown with only country names? export class DataService { constructor() { } getData(): Observable<{name: string, id: string}[]> { return of([ { id: '5da06f27b2dcbf00125c908a', name: 'Pakitan', states: [ { name: 'Value 1', id: 'val1' }, { name: 'Value 2', id: 'val2' }, { name: 'Value 3', id: 'val3' }, { name: 'Value 4', id: 'val4' }, { name: 'Value 5', id: 'val5' } ] } ]); } } Commented Dec 5, 2019 at 5:48
  • 1
    Its working fine. stackblitz.com/edit/angular-jjppcq Commented Dec 5, 2019 at 5:52

1 Answer 1

2

When using mat-select in Reactive form, you need to have FormControl for storing value from form and separate class variable for storing the options array of values.

Component

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';

  form: FormGroup;

  dropdownval = [ ]; // variable to store array (options)

  constructor(private dataService: DataService, private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group({
      dropdown: ['', Validators.required] // form control which will hold form selected value
    });
  }

  onEvent() {
    this.dataService.getData().subscribe(res => {
      console.log(res);
      this.dropdownval = res;
    })
  }
}

Template

<form [formGroup]="form">

    <mat-form-field>
        <mat-label>Select a Country</mat-label>
    <!-- dropdown is used for formcontrol value -->
        <mat-select [formControl]="dropdown">
      <!-- dropdownval holds values for options -->
            <mat-option *ngFor="let item of dropdownval"> 
                {{ item.name }}
            </mat-option>
        </mat-select>
    </mat-form-field>

</form>

I have prepered the Stackblitz. I have considered the case where you are getting data from service. So click on get data first, then you'll see the dropdown populated.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot. I was just making it difficult for myself by using res[0] to specifically target the first object. Its working fine now thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.