Angular Tutorial - Create a Material Upload Component and Save with Firebase
Relaying upload progress engages user attention. Assembling a few Angular Material components and using Firebase storage, the UI can present the ability to start, pause, cancel, and display progress of a file upload.
If you recall the days before the SPA pattern (Single Page Applications) emerged, the traditional method for uploading files posted an entire form. The form wrote the file content to the request stream leaving the browser to appear hanging. There's no difference in the request today, but no simple method existed to relay the progress of the upload back to the user.
Love Web Development?
Angular, Google Cloud, C#, Node, NestJs?
Browser capabilities advanced in the rise of XHR (XMLHttpRequests) and AJAX (Asychronous JavaScript and XML) allowing HTTP requests to be made between browser (client side code) and server. These two advancements opened the ability to observe progress of the request enhancing the user experience.
In this tutorial, you'll learn how to use Angular Material Components that upload files to Firebase.
Goals
1. Model a File Upload Interface and Upload Service Contract
2. Create a Firebase Upload Service
3. Implement a SCAM File Upload Component and Upload List Component
4. Configure the Upload Service Module and AngularFire for Firebase
5. Run the Application and Upload Files
To follow along in this tutorial, use the GumRoad link below.
Free Your Developers
Nx Monorepo Starter alleviates developers from re-inventing deployments on popular Google Cloud Services, standardizes on libraries, and saves time for scaffolding projects.
Thanks for your interest in Nx GCP Starter. We'll be in touch shortly
Create a File Upload Interface and Service Contract
The FileUpload
contains information about the file chosen and the status of the HTTP request.
Property Name | Type | Description |
---|---|---|
id |
string |
A unique identifier for the file upload |
name |
string |
Name of the chosen file |
fileData |
File |
Provides information about files and allows JavaScript in a web page to access their content. |
percentageUploaded |
number |
The percentage the upload is complete |
status |
FileUploadStatus |
The status of the file upload |
contentType |
string |
The content type of the file e.g. image/png |
path |
string? |
Optional path value for where the file will be stored on the server |
The FileUploadStatus
enumerates the status of the file upload including QUEUED
, PAUSED
, CANCELLED
, IN_PROGRESS
, COMPLETE
, and ERROR
.
The upload service contract provides flexibility for choosing an implementation. Firebase
is specific to Google Cloud
, so changing to a different cloud provider, say Amazon's S3
storage, would involve creating another UploadService
that implements the IFileUploadService
interface.
With different UploadService
implementations, swapping the dependency eases the pain of changing a specific reference as we can inject an IFileuploadService
interface. You'll learn to configure this in the Implement File Upload Components and the Configure Upload Service Module sections.
Property / Method | Description |
---|---|
fileUploads$: Observable<Record<string, FileUpload>> |
An observable property that keeps reference of files uploads by their id |
upload(fileUploadId:string):void |
Starts an upload by a specific fileId |
addFiles(fileList: FileList):void |
Adds files to the object of fileUploads$ |
remove(fileUploadId: string):void |
Removes the file from the object of fileUploads$ |
Create Firebase Upload Service
The FirebaseUploadService
handles uploading files to Firebase
.
The AngularFireStorage
class injects into the constructor from the @angular/fire
package. AngularFireStorage
handles the upload into Firebase
. Below, I explain the properties
and methods
of the FireUploadService
.
Property Name | Access Modifier | Type | Description |
---|---|---|---|
tasks |
private |
Record<string, AngularFireUploadTask> |
Keeps track of the AngularFireUploadTasks by object property id . This id maps to a FileUpload.id to access the asks easily in the case to pause, resume, or cancel the upload. |
queudedFiles$ |
private |
BehaviorSubject<Record<string,FileUpload>> |
Keeps track of all the files |
fileUploads |
public / readonly |
Observable<Record<string, FileUpload>> |
Readonly object of FileUploads |
Method | Desciption |
---|---|
addFiles(fileList:FileList): void |
Transforms files to the FileUpload type and adds them to the queuedFiles$ |
upload(fileUploadId: string):void) |
Creates the task in AngularFireStore service and subscribes to the task's snapshotChanges . Updates the queuedFiles$ status changes and percentage completed. |
remove |
Removes a file from the queue and cancels any tasks that may have been running. |
The upload
method finds the file by it's id in the queuedFiles$
property. Using the path
and fileData
, fileStorage
creates an AngularFireTask
by calling the upload
method. The generated task appends to our tasks
property, and the fileUpload
's status updates to IN_PROGRESS
.
The task.snapshotChanges
is an observable that emits the changes in state of the uploading file. The filter
operator in this case checks if the value is notEmpty
and primarily I added this for testing purposes. The map
operator transforms the value emitted to a FileUpload
. Once the observable completes, the finalize
operator removes the current task from the tasks
property.
Subscribing to changes invokes the upload. As values are emitted, the queuedFiles$
property updates the fileUpload
in it's dictionary.
Implement File Upload Components
A couple of SCAM (Single Component Angular Module) modules drive consistency in display and upload service.
The NgServeFileUploadModule
imports the MatProgressModule
to support the FileUploadComponent
presentation component.
The FileUploadComponent
consumes one @Input()
, fileUpload
. It's responsible for displaying the progress, file name, and percentage upload to the user. The mat-progress-bar
reads the fileUpload.percentageUploaded
to reflect upload progress.
The FileUploadListModule
follows the same SCAM pattern. The FileUploadListComponent
depends on the MatButtonModule
and the MatIconModule
and are added to the imports
of the FileUploadListModule
.
The FileUploadListModule
provides flexibility for you to choose which implementation of the IFileUploadService
to consume in the FileUploadListComponent
. The static method configure
defines the IFileUploadService
to be injected into the FileUploadListComponent
.
The FileUploadListComponent
orchestrates the user's actions having them choose files to upload, and start / remove uploads.
In the markup of the FileUploadListComponent
, a list of the files selected displays each file for upload using the ng-serve-file-upload
component. Underneath, two mat-icon-button
s empower the user to start an upload or remove the file from the list.
Toward the bottom of the markup, styling the file input involves hiding the element and invoking the click method from an Angular Material button. On the file
input, notice the identifier #fileListUpload
. When the Choose Files
material button is clicked, the #fileListUpload
's click event invokes in the component typescript chooseFiles
method.
The FileUploadListComponent
fileListUpload
property exposes an ElementRef
to the #fileListUpload
file input. So when the Choose Files
material button click event fires, the chooseFiles
method invokes the click event on the #fileListUpload
file input.
Property | Description |
---|---|
fileListUpoad:ElementRef |
Makes a reference to the file input in the component |
UPLOAD_STATUS |
A reference to the FileUploadStatus to compare the file status to display / hide buttons. |
fileUploads$ |
The files stored in state of the IFileUploadService |
fileTypes |
An @Input() that controls file selection e.g. images/* as defined by the consuming component |
Method | Description |
---|---|
chooseFiles |
Invokes the fileListUpload click event to open the list of files to choose |
filesSelected |
An event handler for the change in files from the fileListUpload(evt:Event) . Adds files to the fileUploadService |
uploadFile(fileUploadId: string) |
Invokes the upload for a particular file |
remove(fileUploadId: string) |
Removes the file state of the file upload service |
Configure Upload Service Module
Configuring the upload service empowers you to choose the implementation of a file upload service. Recall this pattern offers flexibility for you swap upload services that implement the IFileUploadService
contract.
Below the NgServeIoFeatureModule
configures the FirebaseFileUploadService
.
The application module lazy loads the NgServeIoFeatureFormModule
through route configuration via the forms
route. Ensure that the AngularFireModule
initializes the app with your Firebase
web configuration options. Below you'll find that the Firebase
configuration exists as a property in the environment for the initializeApp
invocation.
If you're unsure where to find the Firebase
configuration properties for your application, go the console, open your project and click the gear icon shown in the graphic below. Scroll down on that page, and the configuration should appear under the SDK setup and configuration
section.
Run the File Upload Application
If you were following along using the Nx Starter Monorepo
, start the application by running the command below.
npx nx serve ng-serve-io-ng-serve
Go to the path http://localhost:4200/forms/file-upload-list. You'll be able to add files, upload to Firebase, and remove files once completed.
Should you find a 403
error on upload, ensure the Rules
allow read/write
for your files. By default, requests made to storage deny unauthorized requests. For testing purposes, set the allow read, write: if true;
. Once you've completed testing, change the rule back to request.auth != null
to prevent unauthorized file uploads.
Summary
You learned how to create an upload component with Angular Material that uploads to Firebase
. Many options exist for unstructured file storage, so an abstraction using the IFileUploadService
interface allows different implementations based on requirements.