0

I am playing around with the HTML5 drag and drop feature in an Angular application. I have the following scenario:

  • A container with some 'objects' that can be dragged
  • A table where the user can drop the dragged elements

I've followed the steps in this tutorial: https://medium.com/@mithun_daa/drag-and-drop-in-angular-2-using-native-html5-api-f628ce4edc3b

And I've created the following app: https://stackblitz.com/edit/angular-vhyax1?embed=1&file=src/app/app.component.html

I've applied the 'makeDraggable' directive to the HTML table so it accepts the drop event. I'd like to know if there's a way to identify the table cell where the element is dropped. I know I could add the 'makeDraggable' directive to each TD cell instead of the parent table, but I'd like to avoid it, as the table can have thousands of cells. Is it possible (and worth it in terms of performance)?

Thanks a lot,

bindi.raval
  • 262
  • 1
  • 7
Fel
  • 4,428
  • 9
  • 43
  • 94

1 Answers1

1

Edit: Alternative solution

I just thought that, instead of looking for a pos_id data attribute when traversing the path array, I can look for 'TD' elements (using the tagName property). Similarly, I can get the parent 'TR'. The cells have a cellIndex property, and the rows have a rowIndex, so I can use these values to look for the cell data in the source object from which the table is built.

Original solution

I don't know if this is the best solution, but it worked for me:

First, in the HTML template, I've added to the droppable cells a pos_id data attribute with the ID of that cell. So they look like:

<tr>
  <td data-pos_id="101"> ... </td>
  <td data-pos_id="102"> ... </td>
  ...
<tr>
<tr>
  <td data-pos_id="201"> ... </td>
  <td data-pos_id="202"> ... </td>
  ...
<tr>

Second, in the makeDroppable directive, inside the drop event listener, I use the path/composedPath property/method to get an array of the DOM elements over which the cursor is. Then, I traverse this array looking for an element that has a pos_id dataset attribute, which will be the element that I'm looking for:

el.addEventListener('drop', (e) => {
  ...

  // Event DOM path
  var path = e.path || (e.composedPath && e.composedPath());

  for(let elem of path) {
    // If the element has a data attribute called 'pos_id', it's a droppable <TD>
    if (elem.dataset && elem.dataset.pos_id) { 
      elem.classList.add('over');
      console.log('Dropped object on position '+elem.dataset.pos_id);

      break;
    }
  }

  ...
}

However, this method has a BIG problem: it relies on a dataset attribute to identify uniquely the position where the user dropped the object, so if the user changes this attribute using the developer console or any other method, the application won't work as expected. I'll post a new question about this subject and I'll keep on doing tests to try to solve this issue.

Fel
  • 4,428
  • 9
  • 43
  • 94
  • `However, this method has a BIG problem: it relies on a dataset attribute to identify uniquely the position where the user dropped the object` This is actually not a big problem, considering drag and drop is a client side functionality. You can always influence the behavior. It doesn't matter if the data is stored in the element dataset or in a script file. Both can be changed because they are client side. – enf0rcer Jul 23 '18 at 09:25
  • Hi, thanks for your answer. Yes, once that the web data is sent to the client, the user can do whatever he likes with it. We always must rely on the server data, not on the client's, because it could be a pain (imagine that the user changes the value of a price field in the frontend and the app uses that price to make an order...) – Fel Jul 23 '18 at 09:49