0
votes

I'm trying to build a complicated reactive form where I need to populate one drop down based on the value of 2 other drop downs. I've figured out how to get values from a service when one dropdown is changed. But it's not going to work for 2 because the second subscription replaces the first. Do I need to find a synchronous way to populate the 3rd drop down? Here's what I have now:

constructor(private fb: FormBuilder, private defectsService: DefectsService) {
  this.searchForm = fb.group(this.searchCriteria);

  ...

  this.tradeParners$ = this.setupCascadingDropDown('community', (communityId: number) => {
    let tradePartnerData$ = this.defectsService
      .getTradePartners(communityId, this.searchForm.get('jobSite').value);
    this.tradeParnersSubscription = tradePartnerData$.do((x) => 
      console.log(`${x} trade partners`)
    ).subscribe();

    return tradePartnerData$;
  });

  this.tradeParners$ = this.setupCascadingDropDown('jobSite', (jobId: number) => {
    let tradePartnerData$ = this.defectsService
     .getTradePartners(this.searchForm.get('community').value, jobId);
    this.tradeParnersSubscription = tradePartnerData$.subscribe();
    return tradePartnerData$;
  });
}

private setupCascadingDropDown(controlName: string, getData): Observable<ISelectItem[]> {
  let control = this.searchForm.get(controlName);
  return control.valueChanges
    .do(test => console.debug(`${controlName}: ${test}`))
    .distinctUntilChanged()
    .switchMap(getData);
}

Is there a way I can subscribe to one observable when one event fires and replace it when a different event fires?

Update

Here's the code for populating the drop down that uses the observable.

<label class="col-sm-2 col-md-2 control-label">Trade Partner:</label>
<div class="col-sm-3 col-md-3">
  <select class="hbp-defect-select" formControlName="tradePartner">
    <option value="-1">- All -</option>
    <option *ngFor="let tp of tradePartners$ | async" [ngValue]="tp.value">{{ tp.text }}</option>
  </select>
</div>
1
There a reason why you are trying to use the same variable?David Aguirre
Is there a way to populate the drop down using 2? I'll update the question with the template code.Colin
Look into subscribing to a behavior subject. Use the .next method to update your behavior subject any time you need to emit a change. tradePartners$ would update anytime the behavior subject updates.David Aguirre
What happens in the first setting of this.tradeParners$ that is needed by (or does not happen in) the second setting of this variable? Perhaps the logic of the two lines should be combined?Richard Matsen
BTW tradeParners$ in the code and tradePartners$ in the template.Richard Matsen

1 Answers

0
votes

It is indeed complicated, but I think I've got my head around it.

const tradeParnersFromCommunity$ = this.setupCascadingDropDown('community', (communityId: number) => {
  let tradePartnerData$ = this.defectsService
    .getTradePartners(communityId, this.searchForm.get('jobSite').value);
  this.tradeParnersSubscription = tradePartnerData$.do((x) => 
    console.log(`${x} trade partners`)
  ).subscribe();

  return tradePartnerDataFromJobsite$;
});

this.tradeParnersFromJobsite$ = this.setupCascadingDropDown('jobSite', (jobId: number) => {
  let tradePartnerData$ = this.defectsService
   .getTradePartners(this.searchForm.get('community').value, jobId);
  this.tradeParnersSubscription = tradePartnerData$.subscribe();
  return tradePartnerData$;
});

this.tradeParners$ = tradeParnersFromCommunity$.merge(tradeParnersFromJobsite$);

You may want a sort in there as well?