PrimeNG Tree Select component with multiple selection don't display preselect datas from edit form

Viewed 173

I have a problem in a edit form with PrimeNG Tree or TreeSelect component (I tried both). The thing is data are provided by an API in the form of entity with an ID, a project, a product and a version. In theory, a project can contains many products and a product can contains many versions (this is why I display in tree). So I transform datas from API in a node model. For the creation form, no problem, but for the edit form, impossible to display preselected datas efficiently. The best I can do is the last childrens (versions) are selected but not parents and parents are not expanded.

component.ts :

  productSoftwareVersionsNodes: TreeNode[] = [];
  productSoftwareVersionsSelected: TreeNode[] = [];

  versionToTreeNode(version: IVersionForNode): TreeNode {
    return {
      label: version.version,
      data: version.id,
      key: version.id!.toString()
    }
  }

  productToTreeNode(product: IProductForNode, project: IProjectForNode): TreeNode {
    const versionsNodes: TreeNode[] = [];

    if(product.versions !== undefined){
      product.versions.forEach(version => versionsNodes.push(this.versionToTreeNode(version)));
    }

    return {
      label: product.product,
      key: project.project + "_" + product.product,
      selectable: false,
      data: product.product,
      children: versionsNodes
    }
  
  }

  projectToTreeNode(project: IProjectForNode): TreeNode {

    const productsNodes: TreeNode[] = [];

    if(project.products !== undefined){
      project.products.forEach(product => productsNodes.push(this.productToTreeNode(product, project)));
    }

    return {
      label: project.project,
      data: project.project,
      key: project.project,
      selectable: false,
      // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
      icon: 'flag-icon flag-icon-' + project.countryCode,
      children: productsNodes
    }
    
  }

  selectedVersionToTreeNode(project: IProjectForNode, product: IProductForNode,version: IVersionForNode): TreeNode {

    const projectNode: TreeNode = {
      label: project.project,
      data: project.project,
      key: project.project,
      selectable: false,
      expanded: true,
      partialSelected: true,
      // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
      icon: 'flag-icon flag-icon-' + project.countryCode
    };

    const productNode: TreeNode = {
      label: product.product,
      data: product.product,
      key: project.project + "_" + product.product,
      selectable: false,
      expanded: true,
      partialSelected: true,
      parent: projectNode
    };

    return {
      label: version.version,
      data: version.id,
      key: version.id!.toString(),
      parent: productNode
    }
  }

  setNodes(inputProductSoftwareVersions: IProjectForNode[]): void {
    inputProductSoftwareVersions.forEach(productSoftwareVersion => this.productSoftwareVersionsNodes.push(this.projectToTreeNode(productSoftwareVersion)));
  }

  setProductSoftwareVersionsSelected(inputProductSoftwareVersions: IProjectForNode[]): void {
    inputProductSoftwareVersions.forEach(project => {
      project.products!.forEach(product => {
        product.versions!.forEach(version => {
          this.productSoftwareVersionsSelected.push(this.selectedVersionToTreeNode(project, product, version));
        })
      })
    });
  }

component.html :

          <p-treeSelect [(ngModel)]="productSoftwareVersionsSelected" display="chip" [metaKeySelection]="false" 
          [ngModelOptions]="{standalone: true}" data-cy="productSoftwareVersion" name="productSoftwareVersion" 
          selectionMode="multiple" id="field_productSoftwareVersion" class="pl-0" type="productSoftwareVersion" 
          aria-describedby="productSoftwareVersion-help" [options]="productSoftwareVersionsNodes" 
          (onNodeUnselect)="nodeUnselected($event)" (onNodeSelect)="nodeSelected($event)">
          </p-treeSelect>

The kind of node I build :

[{ data: "62bbf3bc16403b596805f6ff"
  key: "62bbf3bc16403b596805f6ff"
  label: "1.2.3"
  parent: {
    data: "TEST"
    expanded: true
    key: "TEST"
    label: "TEST"
    parent: {
      data: "TEST2"
      expanded: true
      icon: "flag-icon flag-icon-gb"
      key: "TEST2"
      label: "TEST2"
      partialSelected: true
    }
    selectable: false
    partialSelected: true
    selectable: false
  }
}]

Data from a node selected on click:

[{ data: "62bbf3d816403b596805f701"
  key: "62bbf3d816403b596805f701"
  label: "1.2.4"
  parent: {
    children: (3) [{…}, {…}, {…}]
    data: "TEST"
    expanded: true
    key: "TEST"
    label: "TEST"
    parent: {
      children: (3) [{…}, {…}, {…}]
      data: "TEST2"
      expanded: true
      icon: "flag-icon flag-icon-gb"
      key: "TEST2"
      label: "TEST2"
      parent: undefined
      selectable: false
    }
    selectable: false
  }
}]

If someone has an idea... Thanks !

1 Answers

I found solution. The key is to work with nodes in options and filter them:

  // Preselect nodes for the editForm
  setProductSoftwareVersionsSelected(productSoftwareVersionsList: IProductSoftwareVersion[]): void {
    // Get all productSoftwareVersions id from the editForm in an array
    const productSoftwareVersionIdList: string[]= productSoftwareVersionsList.flatMap(p => p.id?.toString()) as string[];

    // Set the selected nodes by filtering all nodes with the array of ids
    this.productSoftwareVersionsSelected = this.productSoftwareVersionsNodes.flatMap(n => n.children).flatMap(c => c?.children).filter(v => v?.key !== undefined && productSoftwareVersionIdList.includes(v.key)) as TreeNode[];
  }
Related