1

I'm working on an Angular application where I need to implement drag and drop functionality for a menu system with the following structure:

Header menu can have both Group menu and Menu items as children. Group menu can have Menu items as children. Menu items do not have any children. menuType has 3 values

  • Header
  • Group menu
  • Menu

Below is the explanation of relationship between above

  • Header menu can have groupmenu and menu both inside it as its child. '
  • Group: Group menu can have menu as its child Menu:
  • Menu will not have any child to it.

Sample json

const menu: Menu = [{
    id: 3108572,
    name: "Management",
    displayName: "Management",
    menuType: "Header",
    icon: "",
    routerLink: "",
    groupMenu: null,
    headerMenu: null,
    items: [],
    groupItems: [
        {
            id: 3108573,
            name: "Menu Configuration",
            displayName: "Menu Configuration",
            menuType: "Menu",
            icon: "fa fa-gears",
            routerLink: "/menus/business-menu-config",
            groupMenu: null,
            headerMenu: null,
            items: [],
            groupItems: null,
            position: 47,
            menuFor: null,
            defaultMenu: false,
            menuNote: "beta"
        },
        // Add other nested menu items here...
    ],
    position: 42,
    menuFor: null,
    defaultMenu: false,
    menuNote: null
},

{
    "id": 3108571,
    "name": "Landing Pages",
    "displayName": "Landing Pages",
    "menuType": "Menu",
    "icon": "fas fa-plane-arrival",
    "routerLink": "/landingpage",
    "groupMenu": null,
    "headerMenu": null,
    "items": [],
    "groupItems": [],
    "position": 41,
    "menuFor": null,
    "defaultMenu": false,
    "menuNote": null
},
{
    "id": 3108601,
    "name": "Templates",
    "displayName": "Templates",
    "menuType": "Header",
    "icon": "",
    "routerLink": "",
    "groupMenu": null,
    "headerMenu": null,
    "items": [],
    "groupItems": [
        {
            "id": 3108602,
            "name": "SMS Templates",
            "displayName": "SMS Templates",
            "menuType": "Menu",
            "icon": "fas fa-comment-dots",
            "routerLink": "/smstemplate",
            "groupMenu": null,
            "headerMenu": null,
            "items": [],
            "groupItems": null,
            "position": 57,
            "menuFor": null,
            "defaultMenu": false,
            "menuNote": null
        },
        {
            "id": 3108603,
            "name": "Consents",
            "displayName": "Consents",
            "menuType": "Menu",
            "icon": "fas fa-file-signature",
            "routerLink": "/clinical-doc/consents",
            "groupMenu": null,
            "headerMenu": null,
            "items": [],
            "groupItems": null,
            "position": 58,
            "menuFor": null,
            "defaultMenu": false,
            "menuNote": null
        },
        {
            "id": 3108604,
            "name": "Email Templates",
            "displayName": "Email Templates",
            "menuType": "Menu",
            "icon": "fas fa-envelope",
            "routerLink": "/emailtemplates",
            "groupMenu": null,
            "headerMenu": null,
            "items": [],
            "groupItems": null,
            "position": 56,
            "menuFor": null,
            "defaultMenu": false,
            "menuNote": null
        }
    ],
    "position": 55,
    "menuFor": null,
    "defaultMenu": false,
    "menuNote": null
}
];

Below is my code implementation

<div [ngClass]="menu.menuType === 'Group' ? 'group-card-menu': 'child-card'">

  <!-- ------------------------------ Menu card ------------------------------ -->
  <div *ngIf="menu.menuType === 'Menu'" class="menu-card" cdkDrag [cdkDragData]="menu">
    <div class="menu-card-content">
      <div class="menu-box">
        <div class="icon-text-wrapper">
          <span class="icon" *ngIf="menu.icon"><i [class]="menu.icon"></i></span>
          <span class="menu-name">{{menu.name}}</span>
        </div>
        <div class="btn-action">
          <a href="javascript:void(0)" class="mx-2" (click)="moveUpMenu({'menu': menu, 'menus': menus})">
            <i class="fa fa-arrow-up"></i>
          </a>
          <a href="javascript:void(0)" class="mx-2" (click)="moveDownMenu({'menu': menu, 'menus': menus})">
            <i class="fa fa-arrow-down"></i>
          </a>
          <a href="javascript:void(0)" class="mx-2" (click)="removeMenu(menu)">
            <i class="fa fa-trash"></i>
          </a>
        </div>
      </div>
    </div>
  </div>

  <!-- ----------------------------- Group menu ------------------------------ -->
  <div *ngIf="menu.menuType === 'Group'" class="panel-card group-card" cdkDrag [cdkDragData]="menu">
    <div class="panel-card-content">
      <div class="menu-box">
        <div class="icon-text-wrapper">
          <span class="icon" *ngIf="menu.icon"><i [class]="menu.icon"></i></span>
          <span class="menu-name">{{menu.name}}</span>
        </div>
        <div class="btn-action">
          <a href="javascript:void(0)" class="mx-2" (click)="moveUpMenu({'menu': menu, 'menus': menus})">
            <i class="fa fa-arrow-up"></i>
          </a>
          <a href="javascript:void(0)" class="mx-2" (click)="moveDownMenu({'menu': menu, 'menus': menus})">
            <i class="fa fa-arrow-down"></i>
          </a>
          <a href="javascript:void(0)" class="mx-2" (click)="changeMenu(menu)">
            <i class="fa fa-cog"></i>
          </a>
          <a href="javascript:void(0)" class="mx-2" (click)="removeMenu(menu)">
            <i class="fa fa-trash"></i>
          </a>
        </div>
      </div>
      <!-- -------------------------- Repeat menu list --------------------------- -->
      <div id="{{'menu' + menu.id}}" cdkDropList [cdkDropListData]="menu.items" (cdkDropListDropped)="drop($event)" [cdkDropListConnectedTo]="getConnectedList()">
        <ng-container *ngFor="let menuItem of menu.items">
          <app-custom-role-single-menu (deleteMenu)="removeMenu($event)" (updateMenu)="changeMenu($event)" (dropMenu)="drop($event)" (moveUp)="moveUpMenu($event)" (moveDown)="moveDownMenu($event)" [menu]="menuItem" [menusIds]="menusIds" [menus]="menu.items"></app-custom-role-single-menu>
        </ng-container>
      </div>
    </div>
  </div>

  <!-- ----------------------------- Header menu ----------------------------- -->
  <div *ngIf="menu.menuType === 'Header'" class="panel-card header-card" cdkDrag [cdkDragData]="menu">
    <div class="header-card-content">
      <div class="menu-box">
        <div class="icon-text-wrapper">
          <span class="icon" *ngIf="menu.icon"><i [class]="menu.icon"></i></span>
          <span class="menu-name">{{menu.name}}</span>
        </div>
        <div class="btn-action">
          <a href="javascript:void(0)" class="mx-2" (click)="moveUpMenu({'menu': menu, 'menus': menus})">
            <i class="fa fa-arrow-up"></i>
          </a>
          <a href="javascript:void(0)" class="mx-2" (click)="moveDownMenu({'menu': menu, 'menus': menus})">
            <i class="fa fa-arrow-down"></i>
          </a>
          <a href="javascript:void(0)" class="mx-2" (click)="changeMenu(menu)">
            <i class="fa fa-cog"></i>
          </a>
          <a href="javascript:void(0)" class="mx-2" (click)="removeMenu(menu)">
            <i class="fa fa-trash"></i>
          </a>
        </div>
      </div>
      <!-- -------------------------- Repeat Header Menu list --------------------------- -->
      <!-- <div id="{{'menu' + menu.id}}" cdkDropList [cdkDropListData]="menu.items" (cdkDropListDropped)="drop($event)" [cdkDropListConnectedTo]="getConnectedList()">
        <ng-container *ngFor="let menuItem of menu.items">
          <app-custom-role-single-menu (deleteMenu)="removeMenu($event)" (updateMenu)="changeMenu($event)" (dropMenu)="drop($event)" (moveUp)="moveUpMenu($event)" (moveDown)="moveDownMenu($event)" [menu]="menuItem" [menusIds]="menusIds" [menus]="menu.items"></app-custom-role-single-menu>
        </ng-container>
      </div> -->
      <!-- ---------------------- Repeat header group menu ----------------------- -->
      <div id="{{'group' + menu.id}}" cdkDropList [cdkDropListData]="menu.groupItems" (cdkDropListDropped)="drop($event)" [cdkDropListConnectedTo]="getConnectedList()">
        <ng-container *ngFor="let groupItem of menu.groupItems">
          <app-custom-role-single-menu (deleteMenu)="removeMenu($event)" (updateMenu)="changeMenu($event)" (dropMenu)="drop($event)" (moveUp)="moveUpMenu($event)" (moveDown)="moveDownMenu($event)" [menu]="groupItem" [menusIds]="menusIds" [menus]="menu.groupItems"></app-custom-role-single-menu>
        </ng-container>
      </div>
    </div>
  </div>
</div>

below is my ts

dropMenu(event: any) {
    console.log(
      'Drop Menu =>',
      event.container.data,
      event.previousIndex,
      event.currentIndex
    );
    if (event.previousContainer === event.container) {
      moveItemInArray(
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    } else {
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    }

    // this.updateMenuOrder();
  }

getConnectedList(): any[] {
    const ids: any[] = ['menu-1', 'group-1', 'header-1'];
    this.menusIds.forEach((x: any) => ids.push(x));
    return ids;
  }

Issue:

am able to drag and drop Menu items inside Group menu and move a Menu item to a Group menu. However, I am unable to move a Menu item to a Header menu or move a Group menu to a Header menu using drag and drop.

What could be causing this issue and how can I resolve it?

Any help would be greatly appreciated!

0