In this article, I’ll walk you through the process of creating a reactive form that includes a file upload, along with the normal form fields. Along the way, we’ll create a custom form control for our file input, add validation and create custom RxJS operators.

Our demo will include a simple sign up form with a single text field, for the user’s email, and an upload file field, where we’ll require the user to upload a profile photo.

Here is an illustration of the final result:

Let’s get started.

Creating the Form

First let’s create the sign-up form:

We’ve created a form group that contains the email and image , and defined both as required. In addition to that, for the image control, we added a custom validation for the file type. We’ll see the implementation of this validator later on.

Creating the File Upload Component

Let’s continue with the file upload component:

We registered a change event listener that emits the files that the user uploads. In our case we’re interested only in one file, so we’ll use the item() method, passing the first index to obtain a reference to it.

We also have a progress component that’s responsible for the loading progress UI (code omitted for the sake of brevity).

Creating a Custom Form Control

At this point, we have a problem. Angular doesn’t come with a built-in value accessor for file input, so we’ll get the following error:

Error: No value accessor for form control with name: ‘image’

What Angular means is that it doesn’t know how to connect our component to the form API. Let’s inform Angular on how to do that by creating a custom value accessor:

I’m not going to get into the details of what actually happens here because I have an article dedicated to this topic:

The important thing to note here is that upon file change we set the control value to be the chosen file, and that whenever we call control.patchValue(null) or control.reset() we’re clearing the file input.

Let’s see what we get by listening to the form’s valueChanges event:

Great, our image control contains the file data.

Creating a Custom Validation

As we saw earlier, we want to create a custom validator that allows uploading only files with the png file extension:

Whenever the user uploads a file, we return a true (invalid) value if its extension is not the same as the one we defined in the validator. Otherwise we return null , which indicates that the validation passed.

I’ve omitted the code that shows the errors for the sake of brevity. If you want to learn how you can magically show a control’s errors, read the following article:

Send it to the Server

We’ve reached the point where we have the information we need, so now it’s time to send it to our server. Let’s implement the submit method:

First, we need to use the FormData API in order to construct an object containing the selected value’s fields, so that they can be sent to the server via Ajax.

We create a pure and reusable function that takes an object, in our case the form value, and returns a FormData instance containing it:

Next, we want to get notified about the progress of the upload, so we need to set the reportProgress option to true and the observe option to events .

We then subscribe to our observable to initiate the request, and listen to the different event types over the course of the request lifecycle. Now we can react appropriately depending on the event type.

It’s worth noting that in real life we’d opt to use a service rather than using the HTTP service directly in the component.

Room for Improvement

I want to take the code a step further and make it reusable, and also write it in a reactive way. Let’s create two custom operators to make that possible:

Building your own operator is as simple as writing a function which takes a source observable as an input and returns an output stream. We can compose forms by seamlessly adding our custom operator to other operators.

Now we can modify the existing code as follows:

Much better.

The Server Endpoint

We’ll conclude by showing an example of our server endpoint. In this case, we’ll use Node and express, but the client-side code will work with any server-side implementation.

🔥 Last but Not Least, Have you Heard of Akita?

Akita is a state management pattern that we’ve developed here in Datorama. It’s been successfully used in a big data production environment, and we’re continually adding features to it.

Akita encourages simplicity. It saves you the hassle of creating boilerplate code and offers powerful tools with a moderate learning curve, suitable for both experienced and inexperienced developers alike.

I highly recommend checking it out.

Follow me on Medium or Twitter to read more about Angular, Akita and JS!