
# Drag and drop files

This is an example of how to attach a file to RealGrid using Drag and Drop.  
Drag the files and drop them anywhere in the row.  

In the example, when a file is dropped, the file is encoded in base64 and stored in the grid.  
Process the file by transmitting the information stored in the grid to the server, or use the method of transmitting the file to the server at the time of FileDrop for processing.  

In the example implementation, new attached files were stored and processed in the grid, but converting and retrieving attached files stored on the server to base64 even when **query** may cause problems with server performance or grid performance, so existing registered files You must import it in link form or download it in a separate window.

`※ Does not work in IE.`    
`※ You must implement the server processing part yourself.`

```js
const fields = [
  { fieldName: 'KorName', dataType: 'text' },
  { fieldName: 'Gender', dataType: 'text' },
  { fieldName: 'Age', dataType: 'number' },
  { fieldName: 'Phone', dataType: 'text' },
  { fieldName: 'ProductId', dataType: 'text' },
  { fieldName: 'KorCountry', dataType: 'text' },
  { fieldName: 'OrderDate', dataType: 'datetime', datetimeFormat: 'yyyy-MM-dd', amText: 'AM', pmText: 'PM' },
  { fieldName: 'CardNumber', dataType: 'text' },
  { fieldName: 'Monetary', dataType: 'text' },
  { fieldName: 'StartDate', dataType: 'datetime', datetimeFormat: 'yyyy-MM-dd', amText: 'AM', pmText: 'PM' },
  { fieldName: 'EndDate', dataType: 'datetime', datetimeFormat: 'yyyy-MM-dd', amText: 'AM', pmText: 'PM' },
  { fieldName: 'ToMonth', dataType: 'number' },
  { fieldName: 'Month', dataType: 'number' },
  { fieldName: 'Year', dataType: 'number' },
  { fieldName: 'InterestRate', dataType: 'number' },
  { fieldName: 'SaveCost', dataType: 'number' },
  { fieldName: 'SaveMaturity', dataType: 'number' },
  { fieldName: 'CurrentSave', dataType: 'number' },
  { fieldName: 'FileDownload', dataType: 'text' },
  { fieldName: 'Files', dataType: 'object' }
];

const columns = [
  { name: 'KorName', fieldName: 'KorName', width: '60', header: { text: 'Name' } },
  { name: 'Gender', fieldName: 'Gender', width: '40', header: { text: 'Gender' } },
  { name: 'Age', fieldName: 'Age', width: '40', header: { text: 'Age' }, styleName: 'right-column' },
  { name: 'Phone', fieldName: 'Phone', width: '100', header: { text: 'Phone' } },
  { name: 'ProductId', fieldName: 'ProductId', width: '120', header: { text: 'Product ID' } },
  { name: 'KorCountry', fieldName: 'KorCountry', width: '100', header: { text: 'Investment Country' } },
  { name: 'OrderDate', fieldName: 'OrderDate', width: '100', header: { text: 'Order Date' } },
  { name: 'CardNumber', fieldName: 'CardNumber', width: '140', header: { text: 'Card Number' } },
  { name: 'Monetary', fieldName: 'Monetary', width: '40', header: { text: 'Currency' } },
  {
    name: 'FileDownload',
    fieldName: 'FileDownload',
    width: '160',
    header: { text: 'Download Attachment' },
    editable: false,
    styleName: 'left-column',
    renderer: {
      type: 'html',
      callback: function(grid, cell, w, h) {
        let str = '';
        const dataRow = cell.index.dataRow;
        const fileValue = grid.getDataSource().getValue(dataRow, 'Files');
        
        if (fileValue) {
          for (let i = 0; i < fileValue.length; i++) {
            const fileName = "'" + fileValue[i].fileName + "'";
            const aTag = " <a download='" + fileValue[i].fileName + "' href='data:application/octet-stream;base64," + fileValue[i].base64Data + "'>" + fileValue[i].fileName + '</a><br>';
            str = str + '<a href="javascript:void(0);" class="btn" onclick="fileDelete(this)" datarow=' + dataRow + ' filename=' + fileName + '>X</a>' + aTag;
          }
        }
        return str;
      }
    }
  }
];

const { gridView, dataProvider } = RealGrid.createGrid("realgrid", {
  columns,
  header: {
    height: 40
  },
  display: {
    rowHeight: -1,
    minRowHeight: 35,
    emptyMessage: "No data to display.",
    fitStyle: 'evenFill'
  },
  edit: {
    insertable: true,
    appendable: true
  },
  footer: {
    height: 40
  },
  stateBar: {
    width: 16
  },
  onInitialized: (grid) => {
    fetch('/data/en/simple_data.json')
      .then(response => response.json())
      .then(data => {
        grid.getDataSource().setRows(data);
        grid.refresh();
      });
  }
}, {
  fields
});

const gridContainer = gridView.getContainer();
let outputs = [];
let files = [];

function fileTobase64(index, cellIndex) {
  if (files.length > index) {
    const file = files[index];
    const reader = new FileReader();

    reader.onload = function() {
      const text = reader.result;
      const file = files[index];
      
      const outs = {};
      outs['fileName'] = file.name;
      outs['dataURL'] = text;
      outs['base64Data'] = text.substring(text.indexOf(',') + 1);

      outputs.push(outs);
      
      if (files.length > (index + 1)) {
        fileTobase64(index + 1, cellIndex);
      } else {
        dataProvider.setValue(cellIndex.dataRow, 'Files', outputs);
      }
    };
    reader.readAsDataURL(file);
  }
}

function dragEnterHandler(ev) {
  gridContainer.querySelectorAll('.rg-data-cell').forEach(element => {
    element.ondragenter = (event) => {
      element.classList.add('drag-over-cell');
    };
    element.ondragleave = (event) => {
      const relatedTarget = event.relatedTarget;
      if (!relatedTarget || !element.contains(relatedTarget)) {
        element.classList.remove('drag-over-cell');
      }
    };
    element.ondrop = element.ondragover = (event) => {
      event.preventDefault();
    };
  });
}

function dragLeaveHandler(ev) {
  if (!ev.relatedTarget || !gridContainer.contains(ev.relatedTarget)) {
    gridContainer.querySelectorAll('.rg-data-cell').forEach(element => {
      element.classList.remove('drag-over-cell');
    });
  }
}

function dragOverHandler(ev) {
  ev.preventDefault();
}

function dropHandler(ev) {
  ev.preventDefault();
  
  gridContainer.querySelectorAll('.rg-data-cell').forEach(element => {
    element.ondrop = element.ondragenter = element.ondragleave = element.ondragover = null;
    element.classList.remove('drag-over-cell');
  });

  const cellIndex = gridView.mouseToIndex(ev.clientX, ev.clientY, true);

  if (cellIndex.dataRow === -1) {
    alert('Drop the file in the exact location.');
    return false;
  }

  if (ev.dataTransfer.items) {
    outputs = [];
    files = [];
    for (let i = 0; i < ev.dataTransfer.items.length; i++) {
      files.push(ev.dataTransfer.items[i].getAsFile());
    }
    fileTobase64(0, cellIndex);
  }
}

function fileDelete(el) {
  const dataRow = parseInt(el.getAttribute('datarow'));
  let fileName = el.getAttribute('filename');
  // Remove surrounding quotes if present
  if (fileName && fileName.startsWith("'") && fileName.endsWith("'")) {
    fileName = fileName.substring(1, fileName.length - 1);
  }

  let filesData = dataProvider.getValue(dataRow, 'Files');
  filesData = filesData.filter(function(obj) {
    return obj.fileName !== fileName;
  });
  dataProvider.setValue(dataRow, 'Files', filesData);
}

const realgridDiv = document.getElementById('realgrid');
realgridDiv.ondragenter = dragEnterHandler;
realgridDiv.ondragleave = dragLeaveHandler;
realgridDiv.ondrop = dropHandler;
realgridDiv.ondragover = dragOverHandler;

window.gridView = gridView;
window.dataProvider = dataProvider;
```

```js
var outputs = [];
var gridContainer = gridView.getContainer();

function fileTobase64(index, cellIndex) {
     if (files.length > index) {
         var file = files[index];
         var reader = new FileReader();

         reader.onload = function(){
           var text = reader.result;
           var file = files[index];
          
           var outs = {};
           //Add required properties individually
           outs['fileName'] = file.name;
           outs['dataURL'] = text;
           outs['base64Data'] = text.substr(text.indexOf(",")+1);

           outputs.push(outs);
          
           if (files.length > (index+1)) {
             fileTobase64(index+1, cellIndex);
           } else {
             dataProvider.setValue(cellIndex.dataRow, "Files", outputs)
           }

         };
         reader.readAsDataURL(file);
     }
}

function dragEnterHandler(ev) {
  gridContainer.querySelectorAll(".rg-data-cell").forEach(element => {
    element.ondragenter = (event) => {
        element.classList.add("drag-over-cell");
    }
    element.ondragleave = (event) => {
      const relatedTarget = event.relatedTarget;
      if (!relatedTarget || !element.contains(relatedTarget)) {
          element.classList.remove("drag-over-cell");
      }
    }
    element.ondrop = element.ondragover = (event) => {
      event.preventDefault();
    }
  })
}

function dragLeaveHandler(ev) {
  if ( !ev.relatedTarget || !gridContainer.contains(ev.relatedTarget)) {
    gridContainer.querySelectorAll(".rg-data-cell").forEach(element => {
        element.classList.remove("drag-over-cell");
    });
  }
}

function dragOverHandler(ev) {
   ev.preventDefault();
}

function dropHandler(ev) {
   ev.preventDefault();

  gridContainer.querySelectorAll(".rg-data-cell").forEach(element => {
    element.ondrop = element.ondragenter = element.ondragleave = element.ondragover = null;
    element.classList.remove("drag-over-cell");
  });

  var cellIndex = gridView.mouseToIndex(ev.clientX, ev.clientY, true);

  console.log(cellIndex);

  if (cellIndex.dataRow == -1) {
    alert("Please drop the file in the correct location");
    return false;
  }

  if (ev.dataTransfer.items) {
    outputs = [];
    files = [];
    for (var i = 0; i < ev.dataTransfer.items.length; i++) {
      files.push(ev.dataTransfer.items[i].getAsFile());
    }

    fileTobase64(0, cellIndex);
  }
}

function fileDelete(el){
   //Delete newly registered files from the grid
   var dataRow = parseInt(el.getAttribute("datarow"));
   var fileName = el.getAttribute("filename");

   var filesData = dataProvider.getValue(dataRow, "Files");
   filesData = filesData.filter(function( obj ) {
     return obj.fileName !== fileName;
   });

   dataProvider.setValue(dataRow, "Files", filesData);
}
```