Search code examples
angularprimeng

Filter the option selection of dynamic dropdown in reactive form


I have a form and a provision to add the field groups dynamically, initially it shows one field group and Add or X it will add and remove the groups

The form itself contains a dropdown and the scenario I'm currently stuck is at, when I select or deselect the options if should reflect in the sibling dropdowns that got added and also when removed .. The selected option should no more be available for other dropdowns and when removed or changed it should be available again...

I'm keeping a track of selected options with index as:

selectedCities = {}; and onChange of dropdown

modifySelectedCities(val, i) { this.selectedCities[i] = val.value; }

The issue I' currently facing is the dropdown when selected with the option itself would be gone since it is no more in the options, below is the code I tried so far

availableCities(i) {
    console.log('sc', this.selectedCities);
    const av = this.cities
      .map((city) => {
        if (!Object.values(this.selectedCities).includes(city)) {
          return city;
        } else {
          null;
        }
      })
      .filter(Boolean);
    console.log('av', av);
    return av;
  }

<div class="card flex justify-content-center">
  <form [formGroup]="myForm">
    <div formArrayName="fieldsArray" class="form-group-container">
      <div
        *ngFor="let fieldGroup of fieldsArray.controls; let i=index"
        [formGroupName]="i"
        class="form-group-city"
      >
        <p-dropdown
          placeholder="Select city"
          formControlName="city"
          [options]="availableCities()"
          (onChange)="modifySelectedCities($event, i); availableCities(i)"
        >
          <ng-template let-option pTemplate="item">
            <div>{{ option }}</div>
          </ng-template>
        </p-dropdown>
        <i
          class="pi pi-times"
          (click)="removeFields(i)"
          style="font-size: 1.5rem; cursor: pointer; margin-left: 10px"
        ></i>
      </div>
    </div>
    <div style="margin-top: 20px">
      <button pButton type="button" label="Add" (click)="addFields()"></button>
    </div>
  </form>
</div>

I couldn't think of how to get this work, and I hope it is clear on what I'm trying

Attaching the full repro in this Stackblitz link, any help is highly appreciated, Thank you


Solution

  • I created forked STACKBLITZ with possible solution.

    Some points:

    • do not use function for the options property, because it will be called on each change detection cycle, rather update it when needed (initial state, add group, remove group)
    • manage a dictionary of available cities, use it for new groups and update it when needed (initial state, add group, remove group)
    • add disabled state for the button, so it will be disabled when there no available cities or when user has empty dropdown (did not select any city yet)

    I added recalculate function:

    recalculateOptions() {
      this.options.forEach((_, ind) => {
        this.options[ind] = [
          ...Object.keys(this.availableCities),
          ...(this.selectedCities[ind] ? [this.selectedCities[ind]] : []),
        ].sort();
      });
      this.addDisabled =
        !Object.keys(this.availableCities).length ||
        this.fieldsArray.length !== this.selectedCities.length;
    }