DEV Community

Cover image for Creating a Basic Draggable Directive in Angular
sandr0-p
sandr0-p

Posted on • Originally published at code-lens.hashnode.dev on

Creating a Basic Draggable Directive in Angular

In a world full of visually appealing dashboards, the real challenge lies in making them customisable enough to meet your users individual needs. While many dashboards are pretty, they often lack the flexibility for user-centric interaction. In this first article of the Drag. Drop. Engage. series, we'll take the first step toward building an interactive Angular dashboard from scratch, starting with a basic draggable directive. This foundation will set the stage for a dashboard users can tailor to maximise its value and functionality.

To follow the article, you should be familiar with Angular and TypeScript, understand Angular Directives, and have an Angular development environment set up.


So, let's begin by creating the draggable directive.

ng generate directive draggable

Enter fullscreen mode Exit fullscreen mode

Before initialising the directive, lets look at the events and attributes we will set.

Draggable : The draggable attribute informs your browser whether this element can be moved.
Drag Start : The dragstart event is issued whenever we move an HTML element marked with draggable.
Position : The position absolute style allows us to drop the HTML anywhere on the page.
Drag Over : The dragover event is fired when a draggable element is above a valid drop target.
Drop : The drop event is fired once we release the mouse above a valid drop target.

/** 
 * Constructor 
 * Initializes styles, attributes, and event listeners for the element 
 * @param element Injected reference to the element this directive is attached to 
 */
constructor(private element: ElementRef) { 
  this.element.nativeElement.addEventListener('dragstart', 
  this.dragStart.bind(this)); // Add event listener for dragstart 
  this.element.nativeElement.setAttribute('draggable', 'true'); // Make the element draggable 
  this.element.nativeElement.style.position = 'absolute'; // Set the position to absolute document.addEventListener('dragover', 
  this.dragOver.bind(this)); // Add event listener for dragover 
  document.addEventListener('drop', this.drop.bind(this)); // Add event listener for drop
}

Enter fullscreen mode Exit fullscreen mode

With the element and the document being able to listen for the correct events, we can continue to implement the first method: dragStart. This method stores the offset between the top left corner of our draggable element and the position we clicked.

private _offset: { x: number, y: number } = { x: 0, y: 0 };

/** 
 * DragStart event handler * Calculates the offset of the mouse pointer from the top-left corner of the element for correct dropping 
 * @param event DragEvent 
 */
private dragStart(event: DragEvent): void { 
  let elementPos = this.element.nativeElement.getBoundingClientRect(); // Get the position of the element 
  this._offset = { x: elementPos.left - event.clientX, y: elementPos.top - event.clientY }; // Calculate the offset
}

Enter fullscreen mode Exit fullscreen mode

The dragOver method is called whenever the draggable element is above a valid drop target. However, we want to be able to drop the element anywhere. To achieve this, we need to cancel the events default behaviour as we have no valid drop target.

/** 
 * DragOver event handler 
 * Prevents the default behaviour of the event to allow dropping the element anywhere 
 * @param event DragEvent 
 */
private dragOver(event: DragEvent): void { 
  event.preventDefault();
}

Enter fullscreen mode Exit fullscreen mode

Lastly, we implement the drop method. Once again, we must cancel the default behaviour to ensure the drop is successful. Now, we just need to update the position of the draggable element. To do this, we add the _offset to the elements current position. This ensures we drop the draggable element relative to the mouse position, just as we picked it up.

/** 
 * Drop event handler 
 * Drops the element, updating its position to the mouse pointer's position 
 * @param event DragEvent 
 */
private drop(event: DragEvent): void { 
  event.preventDefault(); 

  let x = event.clientX + this._offset.x; // Add the offset to the x position to get the correct position 
  let y = event.clientY + this._offset.y; // Add the offset to the y position to get the correct position 
  this.element.nativeElement.style.left = x + 'px'; // Set the new x position this.element.nativeElement.style.top = y + 'px'; // Set the new y position
}

Enter fullscreen mode Exit fullscreen mode

With the directive wrapped-up, we can use it. Open the app.component.html file and add some valid HTML. I use a Bootstrap Card, just to make it look nice.

💡 Depending on your Angular project, your directive might be called appDraggable.

<div class="card col-2 position-absolute" ngDraggable>
  <div class="card-header"> Drag and Drop </div>
  <div class="card-body"> </div>
</div>

Enter fullscreen mode Exit fullscreen mode

And here is the whole project up and running.


With the foundation laid for creating a basic draggable directive in Angular, you've taken the first step toward building a more interactive and customisable dashboard. Now, it's time to put this knowledge into practice. Try implementing this example in your own project, tweak the code to see how it behaves, and experiment with additional features or improvements. If you'd like, feel free to fork this example and share your variations or enhancements.

Don't hesitate to reach out with questions, insights, or suggestions. Stay tuned for the next article in the series, where we'll continue to elevate your drag-and-drop dashboard by building a visual CSS grid!

Top comments (0)