ngx-storage-firebaseui - Open Source Library for Angular Web Apps to integrate a material user interface for firebase storage.
Angular UI component for firebase authentication. This library is an angular module (including angular components and services) that allows to upload/download or event delete files from firebase storage within a firebase project.
❤️
Built by and for developers Do you have any
question or suggestion ? Please do not hesitate to contact us!
Alternatively, provide a PR | open an appropriate issue here
If you like this project, support ngx-storage-firebaseui
by starring
Table of Contents
- Why to use ngx-storage-firebaseui ?
- Library's components
- Requirements
- Demo
- Screenshots
- Peer Dependencies
- Dependencies
- Firebase Cloud Functions and Firebase extensions
- Installation
- Usage
- API
- Run Demo App Locally
- Other Angular Libraries
- Support
- License
Why to use ngx-storage-firebaseui ?
💝 it uses a responsive and accessible web design UX/UI from google material concepts and components (supporting desktop, tablet and mobile view) incl. smooth animations for a better UX.💄 pick up your own theme! change the primary, accent and warn colors whenever you need (e.g to support light and dark themes)🚢 super easy to use with an angular based project (project that is created with the angular-cli)🔜 optional configuration♻️ configure your components in runtime♻️ reusable components for every project that needs some uploads workflows with a firebase project/app.🛃 built in feedback mechanism in form of a snackbar when an error orany
important event occurred.🆘 ability to delete files👻 compatible withngx-auth-firebaseui
learn more🔥 Sync user's uploads with FIRESTORE AUTOMATICALLY🎉 Supports SSR - Server Side Rendering🔜 support of i18n
Library's components
<ngx-storage-firebaseui-images>
used to upload imaged to firebase storage and sync them with firestore
Supported Processes/Workflows and Actions:
- upload images
- image grid
- delete image (soon)
- pre-built firebase functions to auto-rotate images (soon)
- angular material theme
- angular material icons
- angular cdk - v9.x
- angular material - v9.x
- angular animations - v9.x
- angular router - v9.x
- angular flex-layout v9.0.0-beta.29
- @angular/fire - v5.4.x
- firebase - v7.13.x
the full tutorial guide can be found here
Demo | Features | Examples
Peer Dependencies - please make sure that peerDependencies are installed if you are not using the schematics
"peerDependencies": {
"@angular/core": "^9.x",
"@angular/animations": "^9.x",
"@angular/cdk": "^9.x",
"@angular/flex-layout": "^9.0.0-beta.29",
"@angular/material": "^9.x",
"@angular/fire": "5.4.x",
"firebase": "7.13.x",
}
Dependencies
- Angular (requires Angular 2 or higher, developed with 9.x) - obviously
In the following section, I will show you how to improve the user's experience regarding images
Resize Images
Install firebase extension -- install the storage resize images extension in your firebase project
- set your firebase cloud function's location
- pick either the default bucket or choose another one
- set size images:
50x50,100x100,200x200,500x500,1000x1000,2000x2000
- choose whether to delete the original image (tip: don't do that so that you can regenerate the thumbnails)
- choose the cloud storage path for resized images
thumbs
(see below an example of an image directory in firebase storage)
after installation
required
)
Upload firebase cloud functions to sync the uploaded images to firebase storage with firestore (firebase init
- init firebase cloud function in your firebase project (
typescript
) - copy and paste the below functions
- upload
firbease deploy --only functions
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { ObjectMetadata } from 'firebase-functions/lib/providers/storage';
// tslint:disable-next-line:no-implicit-dependencies
import { Bucket, Storage } from '@google-cloud/storage';
// @ts-ignore
// tslint:disable-next-line:no-implicit-dependencies
import { GetSignedUrlResponse } from '@google-cloud/storage/build/src/file';
const path = require('path');
const os = require('os');
const fs = require('fs');
// tslint:disable-next-line:no-implicit-dependencies
const spawn = require('child-process-promise').spawn;
// tslint:disable-next-line:no-implicit-dependencies
const mkdirp = require('mkdirp-promise');
const storage = new Storage();
const firestore = admin.firestore();
const defaultStorage = admin.storage();
// const defaultBucket = defaultStorage.bucket();
/**
* Firebase cloud function to sync uploaded images to firebase storage bucket with firestore
*
* @author Anthony Nahas
* @since 03.2020
* @version 1.0
*/
export const syncImgsWithFirestore =
functions
.storage
.object()
.onFinalize(async (object: ObjectMetadata) => {
console.log('syncImgsWithFirestore is now running', object);
if (object && object.contentType && !object.contentType.includes('image')) {
console.log('exiting function');
return false;
}
let downloadURLs: any = null;
let downloadURL = null;
try {
downloadURLs = await getDownloadURL(object);
downloadURL = downloadURLs[0];
console.log('Download URL --> ', downloadURL, typeof downloadURL);
// downloadURL = downloadURL[0];
} catch (e) {
console.error('Error while getting download url', e);
}
const filePath = object.name;
console.log('file path -->', filePath);
// @ts-ignore
const pathSegments = filePath.split('/');
console.log('path segments --> ', pathSegments);
const indexOfID = pathSegments.indexOf('imgs');
console.log('indexOfID', indexOfID, pathSegments[indexOfID + 1]);
const imgID = pathSegments[indexOfID + 1];
const lastSegment = pathSegments[pathSegments.length - 1];
console.log('lastSegment', lastSegment);
const nameSegments = lastSegment.split('_');
console.log('nameSegments', nameSegments);
const resize = nameSegments[nameSegments.length - 1];
console.log('resize', resize);
const size = resize.split('x')[0];
console.log('size', size, typeof size);
// const img: NahausImage = {
// id: imgID,
// downloadURL,
// path: filePath
// };
const indexOfThumbs = pathSegments.indexOf('thumbs');
console.log('indexOfThumbs', indexOfThumbs);
let firestorePath: string;
if (indexOfThumbs !== -1) {
// the image is a thumbnail
firestorePath = pathSegments.splice(0, indexOfThumbs).join('/');
console.log('firestorePath', firestorePath);
const thumbsMapKey: string = `thumbs.${size}`;
return await firestore.doc(firestorePath).update({
[thumbsMapKey]: {
id: imgID,
downloadURL,
path: filePath
}
});
} else {
pathSegments.pop();
firestorePath = pathSegments.join('/');
console.log('firestorePath', firestorePath);
return await firestore.doc(firestorePath).set({
id: imgID,
downloadURL,
path: filePath
}, { merge: true });
}
});
function getDownloadURL(object: ObjectMetadata): Promise<GetSignedUrlResponse> | void {
if (!object.name) {
return;
}
const bucket: Bucket = defaultStorage.bucket();
const file = bucket.file(object.name);
// Get a signed URL for the file
return file.getSignedUrl({
action: 'read',
expires: '03-17-2025'
});
}
export const adjustOrientation = functions
.storage
.object()
.onFinalize(async (object) => {
const filePath = object.name;
const bucketName = object.bucket;
const metadata = object.metadata;
const tempLocalFile = path.join(os.tmpdir(), filePath);
const tempLocalDir = path.dirname(tempLocalFile);
const bucket = storage.bucket(bucketName);
if (object && object.contentType && !object.contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
if (metadata && metadata.autoOrient) {
console.log('This is already rotated');
return null;
}
return mkdirp(tempLocalDir).then(() => {
// Download file from bucket.
// @ts-ignore
return bucket.file(filePath).download({ destination: tempLocalFile });
}).then(() => {
console.log('The file has been downloaded to', tempLocalFile);
// Convert the image using ImageMagick.
return spawn('convert', [tempLocalFile, '-auto-orient', tempLocalFile]);
}).then(() => {
console.log('rotated image created at', tempLocalFile);
// @ts-ignore
metadata.autoOrient = true;
return bucket.upload(tempLocalFile, {
destination: filePath,
metadata: { metadata: metadata }
});
}).then(() => {
console.log('image uploaded to Storage at', filePath);
// Once the image has been converted delete the local files to free up disk space.
fs.unlinkSync(tempLocalFile);
console.log('Deleted local file', filePath);
return;
});
});
Installation
(1)1. Install via ng add. (Recommended)
If Angular Material Design is not setup, just run ng add @angular/material
learn more
Now add the library via the angular schematics
ng add ngx-storage-firebaseui
✔️ peer dependencies will be automatically added the package.json and installed✔️ ngx-storage-firebaseui
's module will be automatically imported to the root module (just replacePUT_YOUR_FIREBASE_API_KEY_HERE
with your firebase api key)✔️ ngx-storage-firebaseui
's assets will be automatically added theangular.json
file
2. Install via npm. (Alternative)
Install above dependencies via npm.
Now install ngx-storage-firebaseui
via:
npm install --save ngx-storage-firebaseui
npm i -s @angular/material @angular/cdk @angular/flex-layout @angular/forms @angular/animations @angular/router
Firebase deps
npm i -s firebase @angular/fire
-> continue by following the instructions
here
Once installed you need to import the main module:
import { NgxStorageFirebaseuiModule } from 'ngx-storage-firebaseui';
The only remaining part is to list the imported module in your application module. The exact method will be slightly
different for the root (top-level) module for which you should end up with the code similar to (notice NgxStorageFirebaseuiModule .forRoot()
):
and then from your Angular AppModule
:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
// Import your library
import { NgxStorageFirebaseuiModule } from 'ngx-storage-firebaseui';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
// Specify the ngx-storage-firebaseui library as an import
NgxStorageFirebaseuiModule.forRoot({
apiKey: 'your-firebase-apiKey',
authDomain: 'your-firebase-authDomain',
databaseURL: 'your-firebase-databaseURL',
projectId: 'your-firebase-projectId',
storageBucket: 'your-firebase-storageBucket',
messagingSenderId: 'your-firebase-messagingSenderId'
}),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Other modules in your application can simply import NgxStorageFirebaseuiModule
:
import { NgxStorageFirebaseuiModule } from 'ngx-storage-firebaseui';
@NgModule({
declarations: [OtherComponent, ...],
imports: [NgxStorageFirebaseuiModule, ...],
})
export class OtherModule {
}
SystemJS
Note:If you are using
SystemJS
, you should adjust your configuration to point to the UMD bundle. In your systemjs config file,map
needs to tell the System loader where to look forngx-storage-firebaseui
:
{
'ngx-storage-firebaseui';: 'node_modules/ngx-storage-firebaseui/bundles/ngx-storage-firebaseui.umd.js',
}
Usage
(3)Once the library is imported, you can use its components, directives and pipes in your Angular application:
<ngx-storage-firebaseui-images></ngx-storage-firebaseui-images>
see the usage
API
<ngx-storage-firebaseui-images></ngx-storage-firebaseui-images>
see the api
option | bind | type | default | description |
---|---|---|---|---|
path | Input() |
string |
- | path to store the image into firebase storage as well as in the firestore |
load | Input() |
boolean |
true |
whether to load the uploaded images from firebase |
addButtonTooltipText | Input() |
string |
add picture |
text of the add button tooltip |
onLoad | Output() |
number |
- | called when the images are loaded from firestore |
onImageUploaded | Output() |
number |
- | called when an image is uploaded to firebase storage |
onImageSelectedAtIndex | Output() |
number |
- | called when an image is selected / clicked |
Other Angular Libraries
- ngx-auth-firebaseui
- ngx-linkifyjs
- @angular-material-extensions/password-strength
- @angular-material-extensions/google-maps-autocomplete
- @angular-material-extensions/fab-menu
- @angular-material-extensions/select-country
- @angular-material-extensions/pages
- @angular-material-extensions/link-preview
- @angular-material-extensions/contacts
- @angular-material-extensions/faq
- angular-material-extensions/freelancer-theme
- @firebaseui/ng-bootstrap
Support
- Drop an email to: Anthony Nahas
- or open an appropriate issue
- let us chat on Gitter
Built by and for developers
This project is supported by jetbrains with 1 ALL PRODUCTS PACK OS LICENSE incl. webstorm
License
Copyright (c) 2020 Anthony Nahas. Licensed under the MIT License (MIT)