NgFirebaseFileUpload is an angular library which provides out of the box functionality for uploading files with firebase. I (Diyen) made this library.

Dependencies

Angular (Tested with angular 4, angular 5, angular 6, angular 7)

firebase (Tested with Version 5.5.7)

Prerequisites

Hopefully you should have already set up angular and firebase on your application before seeking out this functionality. Just in case you have not, please

  • Set up angular here
  • Set up firebase here

So from here, you can either head to the npm repository and use it directly or follow me through the building of this functionality.

Create a new angular project

Run ng new project-name

Replace project-name with your actual project name.

From the terminal, move to the project folder

Run cd project-name

Again, replace project-name with your actual project name.

Set up firebase in your project following this link

PLEASE MAKE SURE FIREBASE IS SET UP SUCCESSFULLY BEFORE YOU CONTINUE

Now generate an angular component

Run ng g c ng-firebase-file-upload

Down below is the full code. Please click here to see the full code.

We want this component to be used as an input field everywhere it is available. So we implement the ControlValueAccessor as below.

NB: I have included the DomSanitizer in the component. In this project, once we upload an image, we will display the uploaded image as a background image. Angular requires we sanitize the styles.

import {Component, forwardRef, OnInit} from '@angular/core';
import {
  ControlValueAccessor,
  FormControl,
  NG_VALUE_ACCESSOR,
} from '@angular/forms';
import {DomSanitizer} from '@angular/platform-browser';

@Component({
  selector: 'ng-firebase-file-upload',
  templateUrl: './ng-firebase-file-upload.component.html',
  styleUrls: ['./ng-firebase-file-upload.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NgFirebaseFileUploadComponent),
      multi: true,
    },
  ],
})

export class NgFirebaseFileUploadComponent implements OnInit, ControlValueAccessor {
 
  onChange;
  onTouched;
 
  validateFn: any = () => {};

  constructor(private domSanitizer: DomSanitizer) {}

  ngOnInit() {}

  writeValue(value) {}

  validate(c: FormControl) {}

  registerOnChange(fn: (value: any) => void) {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => void) {
    this.onTouched = fn;
  }

}

We declare the instance variables which we will use to this component. Don’t bother if you don’t understand what these variables do. They will become clear in the end.

@Input() pathFolder = 'media';  // Firebase path for uploading images
@Input() isRequired = false;  // Whether or not this file upload field is required where it is used

percentage = 0;  // Percentage of file uploaded
form: FormGroup;
onChange;
onTouched;
value = '';  // url of the file on firebase
fileField;  // the reactive form field that stores the value
downloadURL;  // duplicate of "value". Helps when you don't want the parent component to have the current value.
backgroundImage;  // This is initially a generic image and later turns to a thumbnail of the file uploaded
fileToUploadId = 'file-to-upload-' + uuid(); // This is used to generate a unique id for the file upload element. 
loading = false;  // Whether or not an upload is in progress
validateFn: any = () => {};

We will now create a reactive form for handling the file upload field when the component is initialized.

ngOnInit() {
    this.fileField = new FormControl(
      this.value, [],
    );

    if (this.isRequired) {
      this.fileField.setValidators([Validators.required]);
    } else {
      this.fileField.setValidators([]);
    }

    this.form = new FormGroup({
      fileField: this.fileField,
    });
}

We also update the validate function after this so that the parent component gets updates

validate(c: FormControl) {
    return this.isRequired ? Validators.required : null;
}

We now create the template

<form [formGroup]="form" class="image-uploaded-wrapper">
  <progress *ngIf="loading" value="{{ percentage }}" max="100" id="uploader">{{ percentage }}%</progress>
  <input type="file" [disabled]="loading" value="" class="file-to-upload-file"
    id="{{ fileToUploadId }}" (change)="uploadFile(file)" #file>
  <input formControlName="fileField" type="hidden" />
  <label
    for="{{ fileToUploadId }}"
    class="uploaded-image-wrapper">
    <div *ngIf="!loading && !(this.downloadURL && this.downloadURL != null && this.downloadURL !== '')" class="uploaded-image-text">Upload image</div>
    <div class="uploaded-image" [style.backgroundImage]="backgroundImage"></div>
  </label>
</form>

And then we add the styles. The styles below with css so that everyone can follow. You can modify to SCSS, SASS, LESS, etc.

Also the long background-image text will produce an image. So feel free to modify or not.

If you prefer to use the image, here it is:

.uploaded-image-wrapper {
  width: 100%;
  padding-top: 100%;
  background-image: linear-gradient(rgba(0,0,0,.3), rgba(0,0,0,.1));
  display: inline-block;
  position: relative;
}

.uploaded-image-wrapper .uploaded-image {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1;
  background-size: cover;
}

.uploaded-image-wrapper:hover {
  cursor: pointer;
}

.uploaded-image-wrapper::after {
  content: "";
  position: absolute;
  width: 64px;
  height: 64px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-image: url('data:image/png;base64, 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');
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
}

.uploaded-image-wrapper .uploaded-image-text {
  position: absolute;
  text-align: center;
  width: 100%;
  bottom: 15px;
  left: 0;
  font-size: 12px;
}

.image-uploaded-wrapper {
  width: 150px;
  display: inline-block !important;
  position: relative;
}

.image-uploaded-wrapper  progress {
  position: absolute;
  width: 80%;
  bottom: 18px;
  margin-left: 10%;
  margin-right: 10%;
}

.file-to-upload-file {
  display: none;
}

You may notice the uploadFile(file) function does not exist. Let us now create it.

uploadFile(file) {

    this.loading = true;

    this.percentage = 0;

    // get file
    const file1 = file.files[0];

    // create storage ref
    const storageRef = firebase.storage().ref(this.pathFolder + '/' + file1.name);

    // Upload file
    const task = storageRef.put(file1);

    // Upload progress bar
    task.on('state_changed',
      (snapshot: any) => {
        this.percentage = (task.snapshot.bytesTransferred / task.snapshot.totalBytes) * 100;
      },
      (error) => {
        console.error(error);
        this.loading = false;
      },
      () => {
        storageRef.getDownloadURL().then((downloadURL) => {
            this.downloadURL = downloadURL;
            this.backgroundImage = (this.downloadURL && this.downloadURL != null && this.downloadURL !== '')
              ? this.domSanitizer.sanitize(SecurityContext.STYLE, 'url(' + this.downloadURL + ')')
              : this.domSanitizer.sanitize(SecurityContext.STYLE, 'none');
            this.onChange(downloadURL);
            this.loading = false;
          },
          (error) => {
            console.error('upload rejected', error);
            this.loading = false;
          });
      }
    );
  }

We now have to update the writeValue function so that we can receive updates on the parent component.

writeValue(value) {
    if (value === '') {
      value = '';
    }
    this.value = value;
    this.downloadURL = value;
    this.backgroundImage = (this.downloadURL && this.downloadURL != null && this.downloadURL !== '')
      ? this.domSanitizer.sanitize(SecurityContext.STYLE, 'url(' + this.downloadURL + ')')
      : this.domSanitizer.sanitize(SecurityContext.STYLE, 'none');
    this.form.get('fileField').setValue(this.value);
  }

And that’s all. We can now use our component in both reactive and template driven forms. Eg:

<ng-firebase-file-upload formControlName="fileField"></ng-firebase-file-upload>
<ng-firebase-file-upload [(ngModel)]="avatar"></ng-firebase-file-upload>

Below is the full code:

ng-firebase-file-upload.component.ts

// ng-firebase-file-upload.component.ts

import {Component, forwardRef, Input, OnInit, SecurityContext} from '@angular/core';
import * as firebase from 'firebase';
import {
  ControlValueAccessor,
  FormControl,
  FormGroup, NG_VALUE_ACCESSOR,
  Validators,
} from '@angular/forms';
import {DomSanitizer} from '@angular/platform-browser';
import {v4 as uuid} from 'uuid';

@Component({
  selector: 'ng-firebase-file-upload',
  templateUrl: './ng-firebase-file-upload.component.html',
  styleUrls: ['./ng-firebase-file-upload.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NgFirebaseFileUploadComponent),
      multi: true,
    },
  ],
})
export class NgFirebaseFileUploadComponent implements OnInit, ControlValueAccessor {

  @Input() pathFolder = 'media';
  @Input() isRequired = false;

  percentage = 0;
  form: FormGroup;
  onChange;
  onTouched;
  value = '';
  fileField;
  downloadURL;
  backgroundImage;
  fileToUploadId = 'file-to-upload-' + uuid();
  loading = false;
  validateFn: any = () => {};

  constructor(private domSanitizer: DomSanitizer) {
  }

  ngOnInit() {
    this.fileField = new FormControl(
      this.value, [],
    );

    if (this.isRequired) {
      this.fileField.setValidators([Validators.required]);
    } else {
      this.fileField.setValidators([]);
    }

    this.form = new FormGroup({
      fileField: this.fileField,
    });
  }

  uploadFile(file) {

    this.loading = true;

    this.percentage = 0;

    // get file
    const file1 = file.files[0];

    // create storage ref
    const storageRef = firebase.storage().ref(this.pathFolder + '/' + file1.name);

    // Upload file
    const task = storageRef.put(file1);

    // Upload progress bar
    task.on('state_changed',
      (snapshot: any) => {
        this.percentage = (task.snapshot.bytesTransferred / task.snapshot.totalBytes) * 100;
      },
      (error) => {
        console.error(error);
        this.loading = false;
      },
      () => {
        storageRef.getDownloadURL().then((downloadURL) => {
            this.downloadURL = downloadURL;
            this.backgroundImage = (this.downloadURL && this.downloadURL != null && this.downloadURL !== '')
              ? this.domSanitizer.sanitize(SecurityContext.STYLE, 'url(' + this.downloadURL + ')')
              : this.domSanitizer.sanitize(SecurityContext.STYLE, 'none');
            this.onChange(downloadURL);
            this.loading = false;
          },
          (error) => {
            console.error('upload rejected', error);
            this.loading = false;
          });
      }
    );
  }

  writeValue(value) {
    if (value === '') {
      value = '';
    }
    this.value = value;
    this.downloadURL = value;
    this.backgroundImage = (this.downloadURL && this.downloadURL != null && this.downloadURL !== '')
      ? this.domSanitizer.sanitize(SecurityContext.STYLE, 'url(' + this.downloadURL + ')')
      : this.domSanitizer.sanitize(SecurityContext.STYLE, 'none');
    this.form.get('fileField').setValue(this.value);
  }

  validate(c: FormControl) {
    return this.isRequired ? Validators.required : null;
  }

  registerOnChange(fn: (value: any) => void) {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => void) {
    this.onTouched = fn;
  }

}

ng-firebase-file-upload.component.html

<!-- ng-firebase-file-upload.component.html -->

<form [formGroup]="form" class="image-uploaded-wrapper">
  <progress *ngIf="loading" value="{{ percentage }}" max="100" id="uploader">{{ percentage }}%</progress>
  <input type="file" [disabled]="loading" value="" class="file-to-upload-file"
    id="{{ fileToUploadId }}" (change)="uploadFile(file)" #file>
  <input formControlName="fileField" type="hidden" />
  <label
    for="{{ fileToUploadId }}"
    class="uploaded-image-wrapper">
    <div *ngIf="!loading && !(this.downloadURL && this.downloadURL != null && this.downloadURL !== '')" class="uploaded-image-text">Upload image</div>
    <div class="uploaded-image" [style.backgroundImage]="backgroundImage"></div>
  </label>
</form>

ng-firebase-file-upload.component.css

/* ng-firebase-file-upload.component.css */

.uploaded-image-wrapper {
  width: 100%;
  padding-top: 100%;
  background-image: linear-gradient(rgba(0,0,0,.3), rgba(0,0,0,.1));
  display: inline-block;
  position: relative;
}

.uploaded-image-wrapper .uploaded-image {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1;
  background-size: cover;
}

.uploaded-image-wrapper:hover {
  cursor: pointer;
}

.uploaded-image-wrapper::after {
  content: "";
  position: absolute;
  width: 64px;
  height: 64px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-image: url('data:image/png;base64, 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');
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
}

.uploaded-image-wrapper .uploaded-image-text {
  position: absolute;
  text-align: center;
  width: 100%;
  bottom: 15px;
  left: 0;
  font-size: 12px;
}

.image-uploaded-wrapper {
  width: 150px;
  display: inline-block !important;
  position: relative;
}

.image-uploaded-wrapper  progress {
  position: absolute;
  width: 80%;
  bottom: 18px;
  margin-left: 10%;
  margin-right: 10%;
}

.file-to-upload-file {
  display: none;
}