How can I help you?
Dragging and Moving Dashboard Layout Panels in Angular
4 Mar 202624 minutes to read
The Dashboard Layout component provides dragging functionality to reorder panels within the layout. While dragging a panel, a holder is highlighted beneath the panel indicating where the panel will be positioned upon release. This visual feedback allows users to determine whether to place the panel in the current position or revert to its previous position without disrupting the layout.
When one or more panels collide during dragging, the colliding panels are automatically pushed in the direction where space is available—left, right, top, or bottom. The position changes of these collided panels are updated dynamically during the drag operation, allowing users to determine the optimal placement for the panel being dragged.
The Dashboard Layout triggers the following events during panel dragging operations:
- dragStart - Triggers when panel drag starts
- drag - Triggers when panel is being dragged
- dragStop - Triggers when panel drag stops
The following sample demonstrates dragging and pushing of panels. For example, while dragging the panel 0 over panel 1, these panels get collided and push the panel 1 towards the feasible direction, so that, the panel 0 gets placed in the panel 1 position.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { DashboardLayoutModule } from '@syncfusion/ej2-angular-layouts'
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
imports: [DashboardLayoutModule],
standalone: true,
selector: 'app-root',
template: `
<div class="control-section">
<ejs-dashboardlayout id='defaultLayout' #defaultLayout [columns]='columns' [cellSpacing]='cellSpacing' [panels]='panels'
(dragStart)="onDragStart($this)" (drag)="onDrag($this)" (dragStop)="onDragStop($this)">
</ejs-dashboardlayout>
</div>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
public cellSpacing: number[] = [10, 10];
public columns: number = 5;
public panels: any = [{ 'sizeX': 1, 'sizeY': 1, 'row': 0, 'col': 0, content: '<div class="content">0</div>' },
{ 'sizeX': 3, 'sizeY': 2, 'row': 0, 'col': 1, content: '<div class="content">1</div>' },
{ 'sizeX': 1, 'sizeY': 3, 'row': 0, 'col': 4, content: '<div class="content">2</div>' },
{ 'sizeX': 1, 'sizeY': 1, 'row': 1, 'col': 0, content: '<div class="content">3</div>' },
{ 'sizeX': 2, 'sizeY': 1, 'row': 2, 'col': 0, content: '<div class="content">4</div>' },
{ 'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 2, content: '<div class="content">5</div>' },
{ 'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 3, content: '<div class="content">6</div>' },
];
$this: any = this;
//Dashboard Layout's drag start event function
onDragStart(args: any) {
console.log("Drag start");
}
//Dashboard Layout's drag event function
onDrag(args: any) {
console.log("Dragging");
}
//Dashboard Layout's dragstop event function
onDragStop(args: any) {
console.log("Drag stop");
}
}@import "node_modules/@syncfusion/ej2-base/styles/material3.css";
@import 'node_modules/@syncfusion/ej2-angular-base/styles/material3.css';
@import 'node_modules/@syncfusion/ej2-angular-layouts/styles/material3.css';
.control-section {
margin: 0 auto;
width: 500px;
}
#defaultLayout {
padding: 10px;
}
#defaultLayout .e-panel .e-panel-container .content {
vertical-align: middle;
font-weight: 600;
font-size: 20px;
text-align: center;
line-height: 90px;
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Customizing the Drag Handle
By default, the entire panel acts as the drag handle, allowing the dragging action to occur when clicking anywhere on the panel. However, the dragging handler for panels can be customized using the draggableHandle property to restrict the dragging action to a specific element within the panel.
The following sample demonstrates customizing the dragging handler of panels, where the dragging action occurs only when interacting with the panel header.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { DashboardLayoutModule } from '@syncfusion/ej2-angular-layouts'
import { ChartAllModule, AccumulationChartAllModule } from '@syncfusion/ej2-angular-charts'
import { Component,ViewEncapsulation } from '@angular/core';
@Component({
imports: [ DashboardLayoutModule, ChartAllModule, AccumulationChartAllModule],
standalone: true,
selector: 'app-root',
template: `
<div class="control-section">
<ejs-dashboardlayout [columns]="6" #editLayout [cellSpacing]='cellSpacing' [draggableHandle]='draggableHandle'>
<e-panels>
<e-panel [sizeX]="3" [sizeY]="2" [row]="0" [col]="0">
<ng-template #header>
<div class="header">Product usage ratio</div>
<span class="handler e-icons burg-icon"></span>
</ng-template>
<ng-template #content>
<div id="column">
<ejs-chart id="columnChart" height="162px" [primaryXAxis]='primaryXAxis'>
<e-series-collection>
<e-series [dataSource]="chartData" xName='month' yName='sales' type="Column">
</e-series>
</e-series-collection>
</ejs-chart>
</div>
</ng-template>
</e-panel>
<e-panel [sizeX]="3" [sizeY]="2" [row]="0" [col]="3">
<ng-template #header>
<div class="header">Last year Sales Comparison</div>
<span class="handler e-icons burg-icon"></span>
</ng-template>
<ng-template #content>
<div id="line">
<ejs-chart id="lineChart" height="162px" [primaryXAxis]='primaryXAxis'>
<e-series-collection>
<e-series [dataSource]="lineData" xName='x' yName='y' type="Line">
</e-series>
</e-series-collection>
</ejs-chart>
</div>
</ng-template>
</e-panel>
<e-panel [sizeX]="3" [sizeY]="2" [row]="2" [col]="0">
<ng-template #header>
<div class="header">Sales increase percentage 1</div>
<span class="handler e-icons burg-icon"></span>
</ng-template>
<ng-template #content>
<div id="pie">
<ejs-accumulationchart id="pieChart" height="162px" [legendSettings]="legendSettings" [tooltip]='tooltip'>
<e-accumulation-series-collection>
<e-accumulation-series [dataSource]="piechart" xName="x" yName="y" innerRadius="20%"
name="Browser">
</e-accumulation-series>
</e-accumulation-series-collection>
</ejs-accumulationchart>
</div>
</ng-template>
</e-panel>
<e-panel [sizeX]="3" [sizeY]="2" [row]="2" [col]="3">
<ng-template #header>
<div class="header">Sales increase percentage 2</div>
<span class="handler e-icons burg-icon"></span>
</ng-template>
<ng-template #content>
<div id="pie1">
<ejs-accumulationchart id="pieChart1" [enableAnimation]="false" height="162px"
[tooltip]='tooltip' [legendSettings]='legendSettings'>
<e-accumulation-series-collection>
<e-accumulation-series [dataSource]="piechart1" xName="x" yName="y" radius="70%"
name="Browser">
</e-accumulation-series>
</e-accumulation-series-collection>
</ejs-accumulationchart>
</div>
</ng-template>
</e-panel>
</e-panels>
</ejs-dashboardlayout>
</div>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
public cellSpacing: number[] = [10, 10];
public draggableHandle: string = '.e-panel-header';
public primaryXAxis: Object = { valueType: 'Category' };
public chartData: any[] = [
{ month: 'Jan', sales: 35 }, { month: 'Feb', sales: 28 },
{ month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
{ month: 'May', sales: 40 }, { month: 'Jun', sales: 32 },
{ month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
{ month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
];
public lineData: any[] = [
{ x: 2013, y: 28 }, { x: 2014, y: 25 }, { x: 2015, y: 26 }, { x: 2016, y: 27 },
{ x: 2017, y: 32 }, { x: 2018, y: 35 },
];
public piechart: any[] = [
{ x: 'TypeScript', y: 13, text: 'TS 13%' },
{ x: 'React', y: 12.5, text: 'Reat 12.5%' },
{ x: 'MVC', y: 12, text: 'MVC 12%' },
{ x: 'Core', y: 12.5, text: 'Core 12.5%' },
{ x: 'Vue', y: 10, text: 'Vue 10%' },
{ x: 'Angular', y: 40, text: 'Angular 40%' }
];
public piechart1: any[] = [
{ 'x': 'Chrome', y: 37, text: '37%' },
{ 'x': 'UC Browser', y: 17, text: '17%' },
{ 'x': 'iPhone', y: 19, text: '19%' },
{ 'x': 'Others', y: 4, text: '4%' },
{ 'x': 'Opera', y: 11, text: '11%' },
{ 'x': 'Android', y: 12, text: '12%' }
];
public legendSettings: Object = {
visible: false
};
tooltip: any;
}@import "node_modules/@syncfusion/ej2-base/styles/material3.css";
@import 'node_modules/@syncfusion/ej2-angular-base/styles/material3.css';
@import 'node_modules/@syncfusion/ej2-angular-layouts/styles/material3.css';
.control-section {
margin: 0 auto;
width: 600px;
}
.e-dashboard-layout.e-control .e-panel .e-panel-container .e-panel-header {
color: rgba(0, 0, 0, 0.61);
}
.e-panel .e-header-text {
padding: 12px 0 12px 0;
}
.e-dashboard-layout.e-control .e-panel:hover {
border: 0px;
}
.e-dashboard-layout.e-control .e-panel .e-panel-header {
font-size: 15px;
font-weight: 500;
height: 37px;
padding: 10px;
vertical-align: middle;
border-bottom: 0.5px solid rgba(0, 0, 0, .125);
}
.e-panel-header {
padding: 10px;
margin-bottom: 0;
background-color: rgba(0, 0, 0, .03);
}
.e-dashboard-layout.e-control .e-panel .e-panel-header {
height: 30px
}
.e-dashboard-layout.e-control .e-panel .e-panel-header div {
text-align: center;
}
.header {
padding: 5px;
display: inline-block;
}
.e-panel-content {
height: 162px;
}
@font-face {
font-family: 'e-custom';
src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAAKAIAAAwAgT1MvMjciQ6oAAAEoAAAAVmNtYXBH1Ec8AAABsAAAAHJnbHlmKcXfOQAAAkAAAAg4aGVhZBLt+DYAAADQAAAANmhoZWEHogNsAAAArAAAACRobXR4LvgAAAAAAYAAAAAwbG9jYQukCgIAAAIkAAAAGm1heHABGQEOAAABCAAAACBuYW1lR4040wAACngAAAJtcG9zdEFgIbwAAAzoAAAArAABAAADUv9qAFoEAAAA//UD8wABAAAAAAAAAAAAAAAAAAAADAABAAAAAQAAlbrm7l8PPPUACwPoAAAAANfuWa8AAAAA1+5ZrwAAAAAD8wPzAAAACAACAAAAAAAAAAEAAAAMAQIAAwAAAAAAAgAAAAoACgAAAP8AAAAAAAAAAQPqAZAABQAAAnoCvAAAAIwCegK8AAAB4AAxAQIAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA4QLhkANS/2oAWgPzAJYAAAABAAAAAAAABAAAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAAAAAAgAAAAMAAAAUAAMAAQAAABQABABeAAAADgAIAAIABuEC4QnhD+ES4RvhkP//AADhAuEJ4QvhEuEa4ZD//wAAAAAAAAAAAAAAAAABAA4ADgAOABYAFgAYAAAAAQACAAYABAADAAgABwAKAAkABQALAAAAAAAAAB4AQABaAQYB5gJkAnoCjgKwA8oEHAAAAAIAAAAAA+oDlQAEAAoAAAEFESERCQEVCQE1AgcBZv0mAXQB5P4c/g4Cw/D+lwFpAcP+s24BTf6qbgAAAAEAAAAAA+oD6gALAAATCQEXCQEHCQEnCQF4AYgBiGP+eAGIY/54/nhjAYj+eAPr/ngBiGP+eP54YwGI/nhjAYgBiAAAAwAAAAAD6gOkAAMABwALAAA3IRUhESEVIREhFSEVA9b8KgPW/CoD1vwq6I0B64wB640AAAEAAAAAA+oD4QCaAAABMx8aHQEPDjEPAh8bIT8bNS8SPxsCAA0aGhgMDAsLCwoKCgkJCQgHBwYGBgUEBAMCAgECAwUFBggICQoLCwwMDg0GAgEBAgIDBAMIBiIdHh0cHBoZFhUSEAcFBgQDAwEB/CoBAQMDBAUGBw8SFRYYGhsbHB0cHwsJBQQEAwIBAQMEDg0NDAsLCQkJBwYGBAMCAQEBAgIDBAQFBQYGBwgICAkJCgoKCwsLDAwMGRoD4gMEBwQFBQYGBwgICAkKCgsLDAwNDQ4ODxAQEBEWFxYWFhYVFRQUExIRERAOFxMLCggIBgYFBgQMDAwNDg4QDxERERIJCQkKCQkJFRQJCQoJCQgJEhERERAPDw4NDQsMBwgFBgYICQkKDAwODw8RERMTExUUFhUWFxYWFxEQEBAPDg4NDQwMCwsKCgkICAgHBgYFBQQEBQQAAAAAAwAAAAAD8wPzAEEAZQDFAAABMx8FFREzHwYdAg8GIS8GPQI/BjM1KwEvBT0CPwUzNzMfBR0CDwUrAi8FPQI/BTMnDw8fFz8XLxcPBgI+BQQDAwMCAT8EBAMDAwIBAQIDAwMEBP7cBAQDAwMCAQECAwMDBAQ/PwQEAwMDAgEBAgMDAwQE0AUEAwMDAgEBAgMDAwQFfAUEAwMDAgEBAgMDAwQFvRsbGRcWFRMREA4LCQgFAwEBAwUHCgsOEBETFRYXGRocHR4eHyAgISIiISAgHx4eHRsbGRcWFRMREA4LCQgFAwEBAwUHCgsOEBETFRYXGRsbHR4eHyAgISIiISAgHx4eAqYBAgIDBAQE/rMBAQEDAwQEBGgEBAQDAgIBAQEBAgIDBAQEaAQEBAMDAQEB0AECAwMDBAVoBAQDAwMCAeUBAgIEAwQEaAUEAwMDAgEBAgMDAwQFaAQEAwQCAgElERMVFhcZGhwdHh4fICAhIiIhICAfHh4dGxsZFxYVExEQDgsJCAUDAQEDBQcKCw4QERMVFhcZGxsdHh4fICAhIiIhICAfHh4dHBoZFxYVExEQDgsKBwUDAQEDBQcKCw4AAAIAAAAAA9MD6QALAE8AAAEOAQcuASc+ATceAQEHBgcnJgYPAQYWHwEGFBcHDgEfAR4BPwEWHwEeATsBMjY/ATY3FxY2PwE2Ji8BNjQnNz4BLwEuAQ8BJi8BLgErASIGApsBY0tKYwICY0pLY/7WEy4nfAkRBWQEAwdqAwNqBwMEZAURCXwnLhMBDgnICg4BEy4mfQkRBGQFAwhpAwNpCAMFZAQSCH0mLhMBDgrICQ4B9UpjAgJjSkpjAgJjAZWEFB4yBAYIrggSBlIYMhhSBhIIrggFAzIfE4QJDAwJhBQeMgQGCK4IEgZSGDIYUgYSCK4IBQMyHxOECQwMAAEAAAAAAwED6gAFAAAJAicJAQEbAef+FhoBzf4zA+v+Ff4VHwHMAc0AAAAAAQAAAAADAQPqAAUAAAEXCQEHAQLlHf4zAc0a/hYD6x7+M/40HwHrAAEAAAAAA/MD8wALAAATCQEXCQE3CQEnCQENAY7+cmQBjwGPZP5yAY5k/nH+cQOP/nH+cWQBjv5yZAGPAY9k/nEBjwAAAwAAAAAD8wPzAEAAgQEBAAAlDw4rAS8dPQE/DgUVDw4BPw47AR8dBRUfHTsBPx09AS8dKwEPHQL1DQ0ODg4PDw8QEBAQERERERUUFBQTExITEREREBAPDw0ODAwLCwkJCAcGBgQEAgIBAgIEAwUFBgYHBwkICQoCygECAgQDBQUGBgcHCQgJCv3QDQ0ODg4PDw8QEBAQERERERUUFBQTExITEREREBAPDw0ODAwLCwkJCAcGBgQEAgL8fgIDBQUHCAkKCwwNDg8PERESExQUFRYWFhgXGBkZGRoaGRkZGBcYFhYWFRQUExIREQ8PDg0MCwoJCAcFBQMCAgMFBQcICQoLDA0ODw8RERITFBQVFhYWGBcYGRkZGhoZGRkYFxgWFhYVFBQTEhERDw8ODQwLCgkIBwUFAwLFCgkICQcHBgYFBQMEAgIBAgIEBAYGBwgJCQsLDAwODQ8PEBARERETEhMTFBQUFREREREQEBAQDw8PDg4ODQ31ERERERAQEBAPDw8ODg4NDQIwCgkICQcHBgYFBQMEAgIBAgIEBAYGBwgJCQsLDAwODQ8PEBARERETEhMTFBQUFRoZGRkYFxgWFhYVFBQTEhERDw8ODQwLCgkIBwUFAwICAwUFBwgJCgsMDQ4PDxEREhMUFBUWFhYYFxgZGRkaGhkZGRgXGBYWFhUUFBMSEREPDw4NDAsKCQgHBQUDAgIDBQUHCAkKCwwNDg8PERESExQUFRYWFhgXGBkZGQAAAQAAAAAD6gPqAEMAABMhHw8RDw8hLw8RPw6aAswNDgwMDAsKCggIBwUFAwIBAQIDBQUHCAgKCgsMDAwODf00DQ4MDAwLCgoICAcFBQMCAQECAwUFBwgICgoLDAwMDgPrAQIDBQUHCAgKCgsLDA0NDv00Dg0NDAsLCgoICAcFBQMCAQECAwUFBwgICgoLCwwNDQ4CzA4NDQwLCwoKCAgHBQUDAgAAABIA3gABAAAAAAAAAAEAAAABAAAAAAABAA0AAQABAAAAAAACAAcADgABAAAAAAADAA0AFQABAAAAAAAEAA0AIgABAAAAAAAFAAsALwABAAAAAAAGAA0AOgABAAAAAAAKACwARwABAAAAAAALABIAcwADAAEECQAAAAIAhQADAAEECQABABoAhwADAAEECQACAA4AoQADAAEECQADABoArwADAAEECQAEABoAyQADAAEECQAFABYA4wADAAEECQAGABoA+QADAAEECQAKAFgBEwADAAEECQALACQBayBlLWljb25zLW1ldHJvUmVndWxhcmUtaWNvbnMtbWV0cm9lLWljb25zLW1ldHJvVmVyc2lvbiAxLjBlLWljb25zLW1ldHJvRm9udCBnZW5lcmF0ZWQgdXNpbmcgU3luY2Z1c2lvbiBNZXRybyBTdHVkaW93d3cuc3luY2Z1c2lvbi5jb20AIABlAC0AaQBjAG8AbgBzAC0AbQBlAHQAcgBvAFIAZQBnAHUAbABhAHIAZQAtAGkAYwBvAG4AcwAtAG0AZQB0AHIAbwBlAC0AaQBjAG8AbgBzAC0AbQBlAHQAcgBvAFYAZQByAHMAaQBvAG4AIAAxAC4AMABlAC0AaQBjAG8AbgBzAC0AbQBlAHQAcgBvAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAHUAcwBpAG4AZwAgAFMAeQBuAGMAZgB1AHMAaQBvAG4AIABNAGUAdAByAG8AIABTAHQAdQBkAGkAbwB3AHcAdwAuAHMAeQBuAGMAZgB1AHMAaQBvAG4ALgBjAG8AbQAAAAACAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwBAgEDAQQBBQEGAQcBCAEJAQoBCwEMAQ0AB2hvbWUtMDELQ2xvc2UtaWNvbnMHbWVudS0wMQR1c2VyB0JUX2luZm8PU2V0dGluZ19BbmRyb2lkDWNoZXZyb24tcmlnaHQMY2hldnJvbi1sZWZ0CE1UX0NsZWFyDE1UX0p1bmttYWlscwRzdG9wAAA=) format('truetype');
font-weight: normal;
font-style: normal;
}
.handler.burg-icon:before {
content: '\e10d';
font-size: 16px;
font-family: 'e-custom';
}
.handler.burg-icon {
float: right;
padding-top: 2%;
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Disabling Panel Dragging
By default, panel dragging is enabled in the Dashboard Layout. The dragging functionality can be disabled using the allowDragging API. Setting allowDragging to false disables the dragging functionality in Dashboard Layout.
The following sample demonstrates a Dashboard Layout with dragging support disabled.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { DashboardLayoutModule } from '@syncfusion/ej2-angular-layouts'
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
imports: [DashboardLayoutModule],
standalone: true,
selector: 'app-root',
template: `
<div class="control-section">
<ejs-dashboardlayout id='defaultLayout' #defaultLayout [columns]='columns' [cellSpacing]='cellSpacing' [panels]='panels'
[allowDragging]='allowDragging'>
</ejs-dashboardlayout>
</div>`,
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
public cellSpacing: number[] = [10, 10];
public columns: number = 5;
public allowDragging: boolean = false;
public panels: any = [
{ 'sizeX': 1, 'sizeY': 1, 'row': 0, 'col': 0, content: '<div class="content">0</div>' },
{ 'sizeX': 3, 'sizeY': 2, 'row': 0, 'col': 1, content: '<div class="content">1</div>' },
{ 'sizeX': 1, 'sizeY': 3, 'row': 0, 'col': 4, content: '<div class="content">2</div>' },
{ 'sizeX': 1, 'sizeY': 1, 'row': 1, 'col': 0, content: '<div class="content">3</div>' },
{ 'sizeX': 2, 'sizeY': 1, 'row': 2, 'col': 0, content: '<div class="content">4</div>' },
{ 'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 2, content: '<div class="content">5</div>' },
{ 'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 3, content: '<div class="content">6</div>' },
];
}@import "node_modules/@syncfusion/ej2-base/styles/material3.css";
@import 'node_modules/@syncfusion/ej2-angular-base/styles/material3.css';
@import 'node_modules/@syncfusion/ej2-angular-layouts/styles/material3.css';
.control-section {
margin: 0 auto;
width: 500px;
}
#defaultLayout {
padding: 10px;
}
#defaultLayout .e-panel .e-panel-container {
vertical-align: middle;
font-weight: 600;
font-size: 20px;
text-align: center;
}
.content {
line-height: 90px;
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Refer to the Angular Dashboard Layout feature tour page for its comprehensive feature representations. Also explore the Angular Dashboard Layout example to see practical implementations of panel manipulation.