1

I have two selects where with one select you choose cars and on another select you get specific models for that car with get request. but when you uncheck option from first select data stays on second select, how can i update data?.

.html

<mat-form-field appearance="fill">
    <mat-select multiple formControlName="Brand" (selectionChange)="getModels($event)" #multiSelect>
      <mat-option *ngFor="let item of vehicles" [value]="item">{{item}}</mat-option>
    </mat-select>
  </mat-form-field>


  <mat-form-field appearance="fill">
    <mat-select multiple formControlName="Model">
      <mat-option *ngFor="let item of inputValue" [value]="item">{{item}}</mat-option>
    </mat-select>
  </mat-form-field>

.ts

  this.campaignService.getVehicles().subscribe((data: any[])=>{
      this.vehicles = data;
      console.log(data);
    })

 // get car models
  getModels(event: any) {
        let value = event.value;
        value.forEach((item: any) => {
        this.campaignService.getModels(item).subscribe((data: any[])=>{
          console.log(data);
          this.inputValue = [...this.inputValue, ...data];
        });
        })
  }
2
  • You need to clear the source variable you use to pre-populate the model form control Commented Feb 24, 2021 at 21:28
  • hi sry, can you be more specific? Commented Feb 24, 2021 at 21:38

2 Answers 2

2

.When you use this:

this.inputValue = [...this.inputValue, ...data];

The value for this.inputValue is previous value + data (new models)

So you should try add this:

this.inputValue = [];

Behind value.forEach like this:

getModels(event: any) {
  let value = event.value;
  this.inputValue = [];
  value.forEach((item: any) => {
    this.campaignService.getModels(item).subscribe((data: any[])=>{
      console.log(data);
      this.inputValue = [...this.inputValue, ...data];
    });
  })
}

This should works, however, you will be making multiple requests to your api, I think this is not good idea. You could try to save localy the temporal object with array data and when you uncheck a option delete this data from this.inputValue.

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

1 Comment

hi! thanks for the answer, as you can see its a multiple select, so when i select more than two options it replaces value for second input, with last chosen options value!
1

Here is how I would do this. Since I don't know what the shape of your data, i just created some, you'll have to adapt to your case.

Note that the answer of @barrilocho is correct, but your problem is that you are using the wrong event. Your selectionChange is triggered each time you check an item, with all the checked items in its event's value, so http calls accumulate.

Try using closed event this way :

<mat-select multiple (closed)="getModels(cars.value)" #cars>

Is is triggered only when the panel close, and therefore iterate over your checked items. closed event is just a small optimization for a bigger problem if you have a lot of data.

Repro on Stackblitz and here is the code :

service :

getModel(car: string) {
    const models = [
      { car: "car1", model: "model1" },
      { car: "car2", model: "model2" },
      { car: "car3", model: "model3" },
      { car: "car3", model: "model4" }
    ];

    // Could be a filter, adapt as you wish
    return of(models.filter(m => m.car === car));
}

TS :

export class SelectValueBindingExample {
  vehicles = ["car1", "car2", "car3"];
  models$: Observable<Array<any>>;

  constructor(private _modelsService: ModelsService) {}

  getModels(selectedCars: Array<any>) {
    const obs$: Array<Observable<any>> = [];
    // Would be better if you could find a way to only request the new items and remove the unckecked ones
    selectedCars.forEach(car => obs$.push(this._modelsService.getModel(car)));
    this.models$ = forkJoin(obs$).pipe(
      map(values => values.flat()) // flatten an array of array
    );
  }
}

HTML :

<mat-form-field appearance="fill">
  <mat-label>Choose your cars</mat-label>
  <mat-select multiple (selectionChange)="getModels(cars.value)" #cars>
    <mat-option *ngFor="let item of vehicles" [value]="item">
      {{item}}
    </mat-option>
  </mat-select>
</mat-form-field>
<br /><br />
<mat-form-field appearance="fill">
  <mat-label>Choose your model</mat-label>
  <mat-select multiple (onClose)="getModels($event)">
    <mat-option *ngFor="let model of models$ | async" [value]="model">
      {{model.car}} - {{model.model}}
    </mat-option>
  </mat-select>
</mat-form-field>

Here I give you a solution using observable directly (Note the async pipe in the html). This is just how I would do it but first, I'd suggest, if possible, to rework your campainService.getModels method to actually accept multiple items and treat everything in the back end. you'll en up with only one http call per changes, which would be way better.

Comments