vendredi 7 juillet 2023

How to implement dynamic filtering in Ember.js dropdowns?

I'm working on an Ember.js application and I need to implement two dropdowns where the options in the second dropdown are filtered based on the selected value in the first dropdown. I have the following requirements:

The second dropdown should fetch its options from an API endpoint. Whenever a value is selected in the first dropdown, the options in the second dropdown should be filtered accordingly. The filtering should happen dynamically without a page refresh. I have tried implementing this functionality, but I'm facing some issues. The second dropdown does not update its options when a new value is selected in the first dropdown. How can I achieve this dynamic filtering behavior?

Here's a simplified version of the code I currently have: I have a parent and then 2 dropdown components inside it. I send the slected value of first dropdown from parent to the second dropdown. But the issue is that the new data is not filtered based on the value of first dropdown (namely: this.SelectedBU (injected from parent)). The component is very complex, thus I am only posting the index.js and index.hbs for the second dropsown.

second_dropdown.hbs



<h3></h3>
<div data-test-product-select>
  
    
      <Inputs::BadgeDropdownList
        @items=
        @listIsOrdered=true
        @onItemClick=
        @selected=
        @placement=
        @isSaving=
        @renderOut=
        @icon=
        class="w-80 product-select-dropdown-list"
        ...attributes
      >
        <:item as |dd|>
          <dd.Action data-test-product-select-badge-dropdown-item>
            <Inputs::TeamSelect::Item
              @product=
              @selected=
            />
          </dd.Action>
        </:item>
      </Inputs::BadgeDropdownList>
    
      <X::DropdownList
        @items=
        @listIsOrdered=true
        @onItemClick=
        @selected=
        @placement=
        @isSaving=
        @renderOut=
        class="w-[300px] product-select-dropdown-list"
        ...attributes
      >
        <:anchor as |dd|>
          <dd.ToggleAction
            class="x-dropdown-list-toggle-select product-select-default-toggle hds-button hds-button--color-secondary hds-button--size-medium"
          >
            <FlightIcon @name= />

            <span
              class="product-select-selected-value
                "
            >
              
            </span>

            
              <span class="product-select-toggle-abbreviation">
                
              </span>
            

            <FlightIcon @name="caret" class="product-select-toggle-caret" />
          </dd.ToggleAction>
        </:anchor>
        <:item as |dd|>
          <dd.Action class="pr-5">
            <Inputs::TeamSelect::Item
              @product=
              @selected=
              @abbreviation=
            />
          </dd.Action>
        </:item>
      </X::DropdownList>
    
  
    <FlightIcon data-test-product-select-spinner @name="loading" />
  
    <div
      class="absolute top-0 left-0"
      
    ></div>
  
</div>

and

second_dropdown.ts

import { assert } from "@ember/debug";
import {action, computed} from "@ember/object";
import { inject as service } from "@ember/service";
import { Placement } from "@floating-ui/dom";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { task } from "ember-concurrency";
import FetchService from "hermes/services/fetch";
import getProductId from "hermes/utils/get-product-id";

interface InputsTeamSelectSignature {
  Element: HTMLDivElement;
  Args: {
    selectedBU: string | null;
    selected?: string;
    onChange: (value: string, attributes?: TeamArea) => void;
    formatIsBadge?: boolean;
    placement?: Placement;
    isSaving?: boolean;
    renderOut?: boolean;
  };
}

type TeamAreas = {
  [key: string]: TeamArea;
};

export type TeamArea = {
  abbreviation: string;
  perDocDataType: unknown;
  BU: string
};

export default class InputsTeamSelectComponent extends Component<InputsTeamSelectSignature> {
  @service("fetch") declare fetchSvc: FetchService;

  @tracked selected = this.args.selected;

  @tracked teams: TeamAreas | undefined = undefined;
  @tracked selectedBU: string | null = null;

  @computed('args.selectedBU')
  get ReFetchTeams() {
    this.selectedBU = this.args.selectedBU;
    return this.fetchteams.perform();
  }

  get icon(): string {
    let icon = "folder";
    if (this.selected && getProductId(this.selected)) {
      icon = getProductId(this.selected) as string;
    }
    return icon;
  }

  get selectedProductAbbreviation(): string | null {
    if (!this.selected) {
      return null;
    }
    const selectedProduct = this.teams?.[this.selected];
    assert("selected Team must exist", selectedProduct);
    return selectedProduct.abbreviation;
  }

  @action onChange(newValue: any, attributes?: TeamArea) {
    this.selected = newValue;
    this.args.onChange(newValue, attributes);
  }

  // @action onBUChange(){
  //   this.selectedBU = this.args.selectedBU;
  //   this.fetchteams.perform();
  // }

  // protected fetchProducts = task(async () => {
  //   try {
  //     let products = await this.fetchSvc
  //       .fetch("/api/v1/products")
  //       .then((resp) => resp?.json());
  //     this.products = products;
  //   } catch (err) {
  //     console.error(err);
  //     throw err;
  //   }
  // });
    protected fetchteams = task(async () => {
      try {
        // Filter the teams based on the selected business unit
        console.log("parent injected value is: ",this.args.selectedBU)
        let teams = await this.fetchSvc
            .fetch("/api/v1/teams")
            .then((resp) => resp?.json());

        // Filter the teams based on the selected business unit
        const filteredTeams: TeamAreas = {};

        for (const team in teams) {
          if (Object.prototype.hasOwnProperty.call(teams, team)) {
            const teamData: TeamArea | undefined = teams[team];
            if (teamData && teamData.BU  === this.args.selectedBU) {
              filteredTeams[team] = teamData;
            }
          }
        }
        console.log("the filtered teams are: ",filteredTeams);
        this.teams = filteredTeams;
          console.log(this.teams);
        } catch (err) {
          console.error(err);
          throw err;
        }
      });

}

declare module "@glint/environment-ember-loose/registry" {
  export default interface Registry {
    "Inputs::TeamSelect": typeof InputsTeamSelectComponent;
  }
}

Any, sort of guidance will be highly appreciated! Thanks!

I have tried using @traked, @did-update etc, but nothing seems to work, even if I remove the "did-insert" the dropdown disappears completely.




Aucun commentaire:

Enregistrer un commentaire