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!