Uploading files with progress monitoring in VanillaJS, Angular and VueJS

Edigleysson Silva
The Startup
Published in
7 min readJun 19, 2020

--

When I was starting my programming journey, I read a book called “Head First — PHP & MySQL”. This book has changed my mind and my life in many ways.

One of the many things I learned in this book was to upload files with PHP and I was amazed by it. I was really ready to create my own social network (who never).

But I wanted more. I had learned PHP, but I wanted a little more asynchrony. I wish I could do what PHP did, but without having that refresh that always bothered me.

After a while I was able to learn JavaScript with AJAX so I was already close, lacking only asynchronous upload.

Recently I worked on some projects that needed a file upload feature. One of these projects was web and I remembered this journey.

So, in this post we will see how to upload files asynchronously with JavaScript with progress monitoring.

Step 1: Our backend

Yes, we will need a backend. But that is not the focus of this post. Here we’ll use a .NET Core Web API backend to receive the file. Below you can see our backend code example.

If you want learn more about .NET Core check this link https://docs.microsoft.com/pt-br/dotnet/core/

But feel free to use the backend language you prefer. Like Node, PHP, Elixir, etc.

Step 2: Understanding FormData

This is a crucial step. FormData is an interface that compile a set of key/value pairs to send using XMLHttpRequest.

This interface is great to use with forms and is essential to send file to backend because it encode data using multipart/form-data that is the enctype to send files to the server.

With a FormData object besides literal values we can add Blob and Files using the append() method.

See this example from Mozilla Developers Network:

var formData = new FormData();

formData.append("username", "Groucho");
formData.append("accountnum", 123456); // number 123456 is immediately converted to a string "123456"

// HTML file input, chosen by user
formData.append("userfile", fileInputElement.files[0]);

// JavaScript file-like object
var content = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file...
var blob = new Blob([content], { type: "text/xml"});

formData.append("webmasterfile", blob);

var request = new XMLHttpRequest();
request.open("POST", "http://foo.com/submitform.php");
request.send(formData);

That we’ll use here.

Step 3: Let's upload a file

It's time. Let's upload a file.

3.1 With VanillaJS

To develop our uploading task we’ll use a simple html that is showed below:

Image 1 — Form upload

This page have a form with an input file, a progressbar element, a heading and finally a paragraph.

The heading and paragraph are to show text progress and result. Check the code:

To our example we’ll use XMLHttpRequest that is used to interact with servers. With this we can update pieces from our page without refresh pages. XMLHttpRequest is used heavily in AJAX programming.

XMLHttpRequest objects have a upload property that returns a XMLHttpRequestUpload object that can be observed to monitoring upload progress. In addition, this object allow to attach events to track progress.

These events are:

  1. loadstart — The upload was begun
  2. progress — Periodically delivered to indicate the amount of progress made so far
  3. abort — The upload operation was aborted
  4. error — The upload failed due to an error.
  5. load — The upload completed successfully
  6. timeout — The upload timed out because a reply did not arrive within the time interval specified by the XMLHttpRequest.timeout.
  7. loadend — The upload finished. This event does not differentiate between success or failure, and is sent at the end of the upload regardless of the outcome. Prior to this event, one of load, error, abort, or timeout will already have been delivered to indicate why the upload ended.

Our JavaScript to upload is shown below:

NOTE: This above code was adapted from https://codepen.io/PerfectIsShit/pen/zogMXP

As you saw we have attached some events. One at XMLHttpRequestUpload that is progress and we added the events to XMLHttpRequest which are: load, error and abort.

To each of these events we have a function to handle it. Let’s focus on progressHandler that is the purpose of this post.

In this function we get a ProgressEvent and we use loaded property, that represents the amount of data sent and total property, that represents the total data to send.

We use these to calculate the progress and update the progressbar using

var percent = Math.round((event.loaded / event.total) * 100);

When the upload is finished the load event is fired and the function completeHandler is executed that show the responseText that came from server and sets the progress to zero.

If we have an error. The error event will be triggered and the errorHandler will be executed and something similar will happen with the cancellation event.

So this is our upload example using Vanilla JS. This example is important and makes it easier to understand the following examples.

3.2 With VueJS

Now we’ll develop our uploading example using Vue. An awesome JavaScript framework.

To develop our example, we’ll use a form like the one already presented here in the Vanilla JS example, but with some changes, because some things in Vue are simpler.

And to send our request we’ll use Axios that is a http client based on Promises and in Vue is a great choice to handle http requests.

To check how to add axios to your Vue project check this link https://www.npmjs.com/package/vue-axios

So in our Home.vue file we have the following code:

First let’s look our form. Note that we using ref to allow us to get the element on script and in the form we attached a submit event using @submit.

In our JavaScript we have the properties status and progressText and we have a method called sendFile.

This method get the selected file and as we already showed we need to use the FormData class and as you can se the process here is the same.

We need to pay attention at post send specifically at uploadProgress property. This property receives function the will get the progress event.

The event here is like the one that as showed at Vanilla. We use loaded and total property to calculate the progress and change the progressbar element and the status text.

Finally, we attach a .then() to Promise to obtain the final result of the request. If you want to catch errors when they occur, you can attach a .catch() to Promise.

3.1 With Angular

Now we’re here. Let’s to do this using Angular. Angular is beautiful framework to develop web applications that is maintained by Google and support (maybe natively) Microsoft’s language called TypeScript.

Angular is a framework or better, a platform and unlike Vue in Angular things are a little more connected, in the sense that the basic tools for development are already present. Tools for form validation, http requests, etc.

So here we will not need to add another tool as we did in Vue when adding axios. Here we use the HttpClienteModule module from @angular/common/http.

In our angular project we have a simple component call home and again, our form is same but using the facilities from Angular. See our home.component.html file below:

We have a submit event attached to our form that call the sendFile method and we have some template reference variable like refs in Vue. In Angular you need to define with a hash (#).

We have the references #progressBar and #inputFile and we have two properties too that are status and progressText.

So let’s look our typescript file:

First look the usage of @ViewChild. This decorato is used to query elements at HTML.

Ate the constructor we injecting the HttpClient to use inside of the sendFile method. In this method we get the file and put in a FormData object (the same here).

An after that we do a POST call sending the FormData object. Note the usage of options at .post() method where we passed two attributes that are observe and reportProgress with values events and true respectively.

Defining observe as events we tell to the http client that we want to receive events that are occurring in our request. And defining resportProgress as true allow us to monitoring events about uploading.

Now that we already know how this works let’s look at subscribe. The .post() returns a Observable and at subscribe we can get the current event.

So we used the switch instruction to check the event type and do something about that. The two events that we using are HttpEventType.UploadProgress and HttpEventType.Response. That are for upload monitoring and final response respectively.

So the UploadProgress event have the same awesome properties that are already presented here: loaded and total. As you may think the calculation is do as the same in Vue and VanillaJS.

So on each upload event we change the progressText and the value of progress HTML element.

Finally in the Response event we get the event.body that came from API and put on status to show at the HTML template.

YEAH!! We finished… But wait, what about ReactJS? So I’m not a React dev, but I think with React you should use a third party library so handle http requests, like axios. So we already have that. If you want to use native XMLHttpRequest we also have that.

So if you is a React dev just do it and share with us. Thank you!!

Summary

Hey, we did it. We now have our upload demo in three ways. With Vue, Vanilla and Angular using different forms with Angular’s XMLHttpRequest, Axios and HttpModule.

This example is very usual, simple and very cool for learning about http requests with JavaScript / TypeScript. If it serves as an exercise, take this example and apply it to an existing project or simply add more beauty / functionality to our example (which is pretty raw).

Finally, you can get all the codes for this post, WebAPI, Vanillar, Angular and Vue by accessing the link https://github.com/geeksilva97/Medium/tree/master/uploading

To see it working, visit my website at https://codesilva.github.io/demos/medium/uploading

That’s all. See ya!

Rerefences

--

--