Categories
Firebase Screencast Tutorials

Monitor Firebase Storage Upload Progress

You can up your UX game by adding things like skeleton loaders, loaders, progress bars to async operations. For example uploading files to a server might take time due to network connection or file size. We can also Monitor Firebase upload progress using @angular/fire package. And that is what we gonna do in this article.

If you are more of a video person that I have also attached the video at the end of the article

Watch Video

For the final source code you can visit the Github repo

Github

The end result of our app would look like as below

Animated shows the whole process of firebase upload progress in Ionic Angular from technbuzz.com

This article has a loose dependence on last two articles where I have discussed how to create a simple todo app with firebase and than implement firebase storage. You can read them to know about the journey or continue with this article as a standalone tutorial.

The starting point of our project is fire storage branch ionic ng todo github branch. You can switch to upload progress branch to get the final result.

The Markup

We will go with the existing markup. We do need some css generated content that is covered next

The CSS

We need to style the loader but question is which one. Ion-loading accepts a cssClass property. We can use that to give it our own class. Let’s add that in our code

    async presentLoading() {
    this.loading = await this.loadingController.create({
      message: 'Please wait...',
      cssClass: 'with-progress'
    });
    return this.loading.present();
  }

Ionic loading component is part our component but not in the dom. It exist somewhere else in the scope of the project. It can be confirmed below. That’s why we can’t style it in our HomePage component.

Firebase Storage Upload Progress uses Ionic Loading Component technbuzz.com
The ion-loading with custom css class is placed at different place than we have imagined

The styles should go in global.scss for this purpose. CSS pseudo-elements can be used to add content to markup without touching HTML. Especially in our case, for aesthetic purpose.

    .with-progress {
  .loading-wrapper{
    position: relative;

    &::after {
      content: "";
      position: absolute;
      width: var(--percent-uploaded);
      height: 5px;
      background-color: var(--ion-color-primary);
      bottom: 0;
      left: 0;
      transition: width 0.5s linear;
    }
  }
}

Remember the days of designing whole layout was built using absolute divs. Somewhat same is happening is here. The CSS generated content here is the bar that shows progress. It’s is absolutely positioned at the bottom of ion-loading. All the magic is happening on the width property. It uses css custom property which start with zero and updated dynamically by JavaScript

Let’s code the firebase upload progress

Most of the modifications are happening in one method i-e uploadFile.

    async uploadFile(id, fileList): Promise {
    if(fileList && fileList.length) {
      try {
        await this.presentLoading();
        const file = fileList[0]

        const task = this.storage.upload(`images/${id}`, file)

        task.percentageChanges().subscribe(resp => {
          this.loading.style.setProperty('--percent-uploaded', `${resp.toFixed()}%`)
        })


      } catch (error) {
        // ...
      }
    } else {
      // ...
    }
  }

The highlighted lines show we are using the upload method of firebase storage to monitor firebase upload progress. We have gave up the put method so let’s adjust accordingly.

Next task percentageChanges method does all the heavy lifting. We just listen to it and update the –percent-uploaded CSS custom property of ion-loading. Remember in the CSS section, this property is inherited by it’s ::after pseudo-element. Hence we style that blue bar to update on the UI.

There is one more thing in our uploadFile method. We need to return the promise when we are done with the upload task. So that we can resume our program execution.

    try {
// ...
return task.snapshotChanges().pipe(
  finalize(() => this.loading.dismiss() )
 ).toPromise()
// ...
}

The getDownloadURL logic is now moved to addTodo method.

    addTodo(){
  this.itemsRef.add({
    // ..
  })
  .then(async resp => {

    const uploadTask: UploadTaskSnapshot = await this.uploadFile(resp.id, this.selectedFile)
    const imageUrl = await uploadTask.ref.getDownloadURL()

    this.itemsRef.doc(resp.id).update({
      // ..
    })
  })
}

This is last change we have made to achieve firebase upload progress. Most of the above code is quite self explanatory. The UploadTask returns a promise which can be queried to get downloadURL.

Final Words

The good thing that we have achieved is the nondestructive feature addition. I have learned how well we can add features to existing code without breaking. Thanks to this Refactoring Book.

Screencast

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.