What is the proper way for binding data to cascade dropdown / autocomplete lists in Angular?

℡╲_俬逩灬. 提交于 2021-01-28 11:14:38

问题


I use mat-autocomplete in several places in my project and fille them as shwon below on form loading:

countries: CountryDto[] = [];
cities: CityDto[] = [];


getCountries() {
  this.demoService.getCountries().subscribe((list: CountryDto) => {
    this.countries = list;
  });
}

getCities(countryId) {
  this.demoService.getCities(countryId).subscribe((list: CityDto) => {
    this.cities= list;
  });
}

However, when I tried to use a second list as cascade, I cannot make the second one to fill according to the first selection. Actually I pass the countryId and then retrieve the city list without any problem. But, I cannot refresh the list of cities. I also tried to use changeDetectorRef.detectChanges() and ngZone.run(), but it does not make any sense. I am not sure if the problem is related to subscribe section, and for this reason I am too confused after trying lots of different tgings to fix the problem. So, first I need to start with a proper approach that is suitable for cascade binding. How can apply a proper way?


回答1:


You are mixing imperative and reactive programming by fetching the data and then assigning the parsed response to a variable. Angular can handle observable values just fine without ever having to subscribe to anything, by instead using the async pipe.

There's nothing wrong with using [(ngModel)] to bind values and to handle changes, but as a rule of thumb, when using Observables to populate the form based on selected values, I like to use only reactive programming with reactive forms instead of model-bindings.

A simple reactive form for you in this case could look something like

  citiesForm = new FormGroup({
    state: new FormControl(),
    city: new FormControl()
  });

with the corresponding HTML

<div [formGroup]="citiesForm">
    <select formControlName="state" >
    .....

This makes it very easy to use the valueChanges event on either the FormGroup or it's individual FormControls.

To demonstrate with a working example, we can replace everything with observable values and a reactive form.

First, we declare our observables that will populate the form.

  states$: Observable<Array<any>>;
  cities$: Observable<Array<any>>;

We initiliaze the observables in the ngOnInit function (note the valueChanges).

  ngOnInit() {
    this.states$ = of(this.states);

    this.cities$ = combineLatest(
      this.citiesForm.get("state").valueChanges, // Will trigger when state changes
      of(this.cities)
    ).pipe(
      map(([selectedState, cities]) =>
        cities.filter(c => c.state === +selectedState) //SelectedState is a string
      )
    );
  }

and lastly bind everything to our form (note the async pipe and the form bindings to our reactive form)

<select formControlName="state" >
  <option value="">Select State</option>
  <option *ngFor="let s of states$ | async" [value]="s.id">{{s.name}}</option>
</select>

Since you mentioned manual triggering of change detection using ChangeDetectorRef in your question, notice that my proposed answer uses the OnPush change detection strategy, and there's no need to think about change detection since the async pipe will know that emission of new values in the observables will affect the template and render it properly.




回答2:


Simplifly the Daniel's idea, you can defined a observable like

cities$=this.citiesForm.get('state').valueChange.pipe(
  switchMap(countryId=>this.demoService.getCities(countryId),
  tap(_=>this.citiesForm.get('city').setValue(null)
)

And use

<options *ngFor="let s of cities$ | async">

Usig ngModel is equal

<select [ngModel]="state" (ngModelChange)="stateChange($event)" >
 ...
</select>


stateChange(countryId)
{
  this.state=state;
  this.cities$=this.demoService.getCities(countryId).pipe(
      tap(_=>this.city=null
  )
}

See that the idea is return an observable and use pipe async in the .html. The operator "tap" make that, when you subscribe -remember that the pipe async is a way to subscribe- the city becomes null



来源:https://stackoverflow.com/questions/65195468/what-is-the-proper-way-for-binding-data-to-cascade-dropdown-autocomplete-lists

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!