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
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; }
Nice module. How would you cleanly override the css or config variables?
Firstly, thank you. You can cleanly override the css in your stylesheet, an example path to the stylesheet will be `src/styles.css`. If you are using the NPM module, there is room for overriding only 2 variables which are `pathFolder` and `isRequired`. If you want to override more configuration, you can follow this tutorial and add whatever it does not yet provide.