Creating a "drag and drop" component for Angular 4+ app from scratch

Recently in a project, I had to create a drag and drop component. Now usually, for everything there is already an existing library in Angular framework, and I found couple of them as well. If you are using Angular Material, your life is even more simplified as Material already has "drag and drop" feature. However, I have two problems with Libraries.

  1. If it is a third party library, you have to be dependent upon the creator's wish for bug fixing and updates. I recently had a problem with another library for multi-select checkboxes, where I used a certain library for multi-select checkboxes which has absolutely beautiful rendition of multi-select checkboxes with absolutely great styles and design. The only problem - when I tried to build it using AoT build, it failed because there were some AoT compilation errors in the library itself. My build server is configured to build using AoT and I couldn't build this application because of this issue. I created a bug with the said library in GitHub, but I don't know when the developer of this library going to take this bug on the priority and fix it.
  2. The customization options become quite limited for the component if you chose to use a third party library instead of writing it on your own. Sure, some developers give you option to use custom styles, but they also end up giving you limited scope of customization more often than not.

Thus, considering these issues, I decided to write my own "Drag & Drop" component instead of using a library for it. This blogpost will explain you the steps to do it yourself.

So here we go -

  1. The HTML part is quite simple to be honest. I have created a drop zone container where user can drop the files. Inside this drop zone, I have an option given to browse the file for those who don't know or want to use the drag and drop feature. Look at the following html code -

  2. Now, we need to show this drop zone as a real drop zone. This we are going to with css styles. Check the stylesheet -

.hcentered {
    text-align: center;
}

.container-row {
    margin-left: 0px;
    margin-right: 0px;
    margin-bottom: 15px;
}

.mediaInput {
    margin: 20px;
}

.progressheader {
    display: inline;
}

.refreshicon {
    margin-bottom: 5px;
    margin-left: 10px;
}

h3 {
    text-align: left;
    font-weight: 500;
}

.box-dragndrop,
 {
    display: none;
}

.box.has-advanced-upload {
    outline: 2px dashed #92b0b3;
    outline-offset: -10px;
    -webkit-transition: outline-offset .15s ease-in-out, background-color .15s linear;
    transition: outline-offset .15s ease-in-out, background-color .15s linear;
}

.box.has-advanced-upload .box-dragndrop {
    display: inline;
}

.box {
    font-size: 1.25rem;
    background-color: #f4f8fc;
    position: relative;
    padding: 50px 20px;
}

.box-icon {
    font-size: 6em;
    fill: #92b0b3;
    display: block;
    margin-bottom: 40px;
    text-align: center;
}

.box-button {
    text-align: center;
    display: block;
    margin: 20px auto 0;
}

.box-file {
    width: 0.1px;
    height: 0.1px;
    opacity: 0;
    overflow: hidden;
    position: absolute;
    z-index: -1;
    text-align: center;
}

.box-file-label-text {
    text-align: center;
    font-size: 1.3em;
}

.box-file-label {
    max-width: 80%;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
    display: inline-block;
    overflow: hidden;
    &:hover {
        color: #e05048;
    }
}

.is-dragover {
    outline-offset: -20px;
    outline-color: #c8dadf;
    background-color: #cdcdcd;
}
  1. And now, it is time to handle the events. To do so, see the html code above. I have registered three events on the drop zone - (drop), (dragover), and (dragleave). On dragover event I am preventing the browser from performing its default action on the dragged object, and then setting a flag to true, which I am using in html to conditionally apply the styles.
public onDragOver(event): void {
  event.preventDefault();
  this.isDragged = true;
}

On dragleave event, I am setting this same flag to false.

public onDragLeave(event): void {
    this.isDragged = false;
  }

And then, most importantly, I am catching the files that are dragged with (drop) event as below -

public onDrop(event: DragEvent): void {
    event.preventDefault();
    this.isDragged = false;
    if (event.dataTransfer.items) {
      // Use DataTransferItemList interface to access the file(s)
      for (var i = 0; i < event.dataTransfer.items.length; i++) {
        // If dropped items aren't files, reject them
        if (
          event.dataTransfer.items[i].kind === "file" &&
          event.dataTransfer.items[i].type == "application/x-zip-compressed"
        ) {
          var file = event.dataTransfer.items[i].getAsFile();
          this.files.push(file);
         }
      }
    }
  }

As you can see, I am only selecting the files that are zip and pushing them to list of files.

And basically that's it. You have the drag and drop component in your Angular app.