I have application that can add a new organization. User can interact using Reactive forms and fill the following requirements (Organization Name, Description, and Image).
Application can add the following inputs (Name and Description) but unfortunately Images cant be add in the database.
The adding of images is possible with Firebase Cloud Functions but unfortunately the functions is not free so I will focus on Firebase Storage instead.
I try this following function but failed:
- In organization.service.ts, I grouped the Image File with Text using FormData and passed it as a request in the Firebase Storage and this error exists. This is the error
organization.service.ts
//For Adding Organization to Firebase
addOrg(name: string, description: string, imageUrl: string){
let generatedId: string;
const newOrg = new Organization(
Math.random().toString(),
name,
description,
imageUrl,
'adm'
);
return this.http
.post<{name: string}>('https://db-student-portal-default-rtdb.firebaseio.com/add-organization.json', {...newOrg, id: null})
.pipe(
switchMap(resData => {
generatedId = resData.name;
return this.allOrg;
}),
take(1),
tap(org => {
newOrg.id = generatedId;
this.organization.next(org.concat(newOrg));
})
);
}
//For Uploading the Image
uploadImage(image: File){
const uploadData = new FormData();
uploadData.append('image', image);
//This is where I want to upload the image. This is Firebase Storage
return this.http.post<{imageUrl: string, imagePath: string}>(
'db-student-portal.appspot.com/',
uploadData);
}
new-org.page.ts - this is the page where the user can add organization or data
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { LoadingController } from '@ionic/angular';
import { switchMap } from 'rxjs/operators';
import { OrganizationService } from '../../organization/organization.service';
//Conversion in base64
function base64toBlob(base64Data, contentType) {
contentType = contentType || '';
const sliceSize = 1024;
const byteCharacters = atob(base64Data);
const bytesLength = byteCharacters.length;
const slicesCount = Math.ceil(bytesLength / sliceSize);
const byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
const begin = sliceIndex * sliceSize;
const end = Math.min(begin + sliceSize, bytesLength);
const bytes = new Array(end - begin);
for (let offset = begin, i = 0; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return new Blob(byteArrays, { type: contentType });
}
@Component({
selector: 'app-new-org',
templateUrl: './new-org.page.html',
styleUrls: ['./new-org.page.scss'],
})
export class NewOrgPage implements OnInit {
formGroup: FormGroup;
constructor(private orgService: OrganizationService,
private router: Router,
private loadingCtrl: LoadingController) { }
ngOnInit() {
this.formGroup = new FormGroup({
orgName: new FormControl(null, {
updateOn: 'change',
validators: [Validators.required]
}),
orgDesc: new FormControl(null, {
updateOn: 'change',
validators: [Validators.required]
}),
imageOrg: new FormControl(null)
});
}
//Adding of Organization in FrontEnd view
onAddOrg(){
if (!this.formGroup.valid || !this.formGroup.value.imageOrg) {
return;
}
this.loadingCtrl.create({
message: 'Creating Organization: ' + this.formGroup.value.orgName,
spinner: 'circular'
}).then(loadingEl => {
loadingEl.present();
this.orgService.uploadImage(this.formGroup.value.imageOrg)
.pipe(switchMap(uploadRes => {
return this.orgService.addOrg(
this.formGroup.value.orgName,
this.formGroup.value.orgDesc,
uploadRes.imageUrl);
}))
.subscribe(() => {
loadingEl.dismiss();
this.formGroup.reset();
this.router.navigateByUrl('/homepage');
});
});
}
//Converting of String to File
onImagePicked(imageData: string | File){
let imageFile;
if (typeof imageData === 'string') {
try {
imageFile = base64toBlob(imageData.replace('data:image/jpeg;base64,', ''), 'image/jpeg');
} catch (error) {
console.log(error);
return;
}
} else {
imageFile = imageData;
}
this.formGroup.patchValue({ imageOrg: imageFile });
}
}
image-picker.component.ts - component that handles the image from local machine
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { Plugins, Capacitor, CameraSource, CameraResultType } from "@capacitor/core";
import { Platform } from '@ionic/angular';
@Component({
selector: 'app-image-picker',
templateUrl: './image-picker.component.html',
styleUrls: ['./image-picker.component.scss'],
})
export class ImagePickerComponent implements OnInit {
@ViewChild('filePicker') filePickerRef: ElementRef<HTMLInputElement>;
@Output() imagePick = new EventEmitter<string | File>();
@Input() showPreview = false;
selectedImage: string;
usePicker = false;
constructor(private platform: Platform) { }
ngOnInit() {
console.log('Mobile: ' + this.platform.is('mobile'));
console.log('Desktop: ' + this.platform.is('desktop'));
console.log('Hybrid: ' + this.platform.is('hybrid'));
console.log('Android: ' + this.platform.is('android'));
console.log('iOS: ' + this.platform.is('ios'));
if ((this.platform.is('mobile') && !this.platform.is('hybrid')) || this.platform.is('desktop')) {
this.usePicker = true;
}
}
//Opens File Manager in local machine
onPickImage(){
if (!Capacitor.isPluginAvailable('Camera') || this.usePicker) {
this.filePickerRef.nativeElement.click();
return;
}
Plugins.Camera.getPhoto({
quality: 50,
source: CameraSource.Prompt,
correctOrientation: true,
height: 320,
width: 300,
resultType: CameraResultType.DataUrl
}).then(image => {
this.selectedImage = image.dataUrl;
this.imagePick.emit(image.dataUrl);
}).catch(error => {
console.log(error);
if (this.usePicker) {
this.filePickerRef.nativeElement.click();
}
return false;
});
}
//preview of image in the UI
onFileChosen(event: Event){
const pickedFile = (event.target as HTMLInputElement).files[0];
if (!pickedFile) {
return;
}
const fr = new FileReader();
fr.onload = () => {
const dataUrl = fr.result.toString();
this.selectedImage = dataUrl;
this.imagePick.emit(pickedFile);
};
fr.readAsDataURL(pickedFile);
}
}
My Dependencies
"dependencies": {
"@angular/common": "~10.0.0",
"@angular/core": "~10.0.0",
"@angular/forms": "~10.0.0",
"@angular/platform-browser": "~10.0.0",
"@angular/platform-browser-dynamic": "~10.0.0",
"@angular/router": "~10.0.0",
"@capacitor/android": "^2.4.5",
"@capacitor/core": "2.4.5",
"@ionic-native/core": "^5.0.0",
"@ionic-native/splash-screen": "^5.0.0",
"@ionic-native/status-bar": "^5.0.0",
"@ionic/angular": "^5.0.0",
"@ionic/pwa-elements": "^3.0.2",
"rxjs": "~6.5.5",
"tslib": "^2.0.0",
"zone.js": "~0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1000.0",
"@angular/cli": "~10.0.5",
"@angular/compiler": "~10.0.0",
"@angular/compiler-cli": "~10.0.0",
"@angular/language-service": "~10.0.0",
"@capacitor/cli": "2.4.5",
"@ionic/angular-toolkit": "^2.3.0",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~3.3.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~3.9.5"
}