Firebase Authentication with Angular - javascript

What I'm Using
Angular 5
AngularFire5
Firebase & Firestore
What I'm Trying to Achieve
I am trying to make a simple authentication/login & registration system. I actually have one already made, though I am running into some issues, and I want to make sure I am going about the best way to setup authentication.
What I have So Far
auth.service.ts
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { AngularFireAuth } from 'angularfire2/auth';
#Injectable()
export class AuthService {
authState: any = null;
email = '';
username = '';
password = '';
errorMessage = '';
error: {name: string, message: string} = {name: '', message: ''};
constructor(private afAuth: AngularFireAuth, private router: Router) {
this.afAuth.authState.subscribe((auth) => {
this.authState = auth
});
}
get isUserEmailLoggedIn(): boolean {
if (this.authState !== null) {
return true
} else {
return false
}
}
get currentUser(): any {
return (this.authState !== null) ? this.authState : null;
}
get currentUserId(): string {
return (this.authState !== null) ? this.authState.uid : ''
}
get currentUserName(): string {
return this.authState['email']
}
signUpWithEmail(email: string, password: string) {
return this.afAuth.auth.createUserWithEmailAndPassword(email, password)
.then((user) => {
this.authState = user
})
.catch(error => {
console.log(error)
throw error
});
}
loginWithEmail(email: string, password: string) {
return this.afAuth.auth.signInWithEmailAndPassword(email, password)
.then((user) => {
this.authState = user
})
.catch(error => {
console.log(error)
throw error
});
}
signOut(): void {
this.afAuth.auth.signOut();
this.router.navigate(['/'])
}
onSignUp(): void {
this.clearErrorMessage()
if (this.validateForm(this.email, this.password)) {
this.signUpWithEmail(this.email, this.password)
.then(() => {
this.router.navigate(['/home'])
}).catch(_error => {
this.error = _error
this.router.navigate(['/register'])
})
}
}
onLoginEmail(): void {
this.clearErrorMessage()
if (this.validateForm(this.email, this.password)) {
this.loginWithEmail(this.email, this.password)
.then(() => this.router.navigate(['/home']))
.catch(_error => {
this.error = _error
this.router.navigate(['/login'])
})
}
}
validateForm(email: string, password: string): boolean {
if (email.length === 0) {
this.errorMessage = 'Please enter Email!'
return false
}
if (password.length === 0) {
this.errorMessage = 'Please enter Password!'
return false
}
if (password.length < 6) {
this.errorMessage = 'Password should be at least 6 characters!'
return false
}
this.errorMessage = ''
return true
}
clearErrorMessage() {
this.errorMessage = '';
this.error = {name: '', message: ''};
}
}
link.service.ts
import { Injectable, OnInit } from '#angular/core';
import { AuthService } from './auth.service';
import { AngularFirestore, AngularFirestoreDocument, AngularFirestoreCollection } from 'angularfire2/firestore';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
export interface Link { uid: string; url: string; shortURL: string; clicks: number }
#Injectable()
export class LinkService implements OnInit {
url: string;
shortURL: string;
showAlert: boolean;
links: Observable<any>;
constructor(public authService: AuthService, private afs: AngularFirestore) {
this.links = afs.collection('Links').valueChanges();
}
ngOnInit() {
}
createShortURL() {
var text = '';
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var length = 6;
for(var i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return this.shortURL = text;
}
addLink() {
if (this.authService.isUserEmailLoggedIn) {
this.createShortURL();
this.afs.collection('Links').doc(this.shortURL).set({
'uid': this.authService.currentUserId,
'url': this.url,
'shortURL': this.shortURL,
'clicks': 0
});
this.clearFields();
this.showAlert = false;
} else {
this.showAlert = true;
}
}
clearFields() {
return this.url = '';
}
}
Where I'm Stuck
With the information provided. I am trying to get the currentUserID in the link.service.ts though it is coming back as undefined. However with the addLink() function, this.authService.isUserEmailLoggedIn works perfectly fine, and Im not sure why it is not returning the correct value otherwise.

You need to get id like this.
this.items = this.itemCollection.snapshotChanges().map(changes => {
return changes.map(a => {
const data = a.payload.doc.data();
data.id = a.payload.doc.id;
return data;
});
});

currentUserId() is a function you should use 'uid': this.authService.currentUserId(),

Related

Sending data with a file in a post request

I am using Angular 11 to send a file to my node / express.js back end how do I send data along with the file?
I have a schema called sources, and another called files the files schema contains the sources schema id in order to indicate which files belong to which sources.
In my angular app I loop over the data fetched from the source's documents to display them, each source displayed has an option to upload a file.
I want to be able to send the source id along with the file in my post request in order to store it on my database.
Here is the code I used :
source.component.ts
#Component({
selector: 'app-source',
templateUrl: './source.component.html',
styleUrls: ['./source.component.scss'],
})
export class SourceComponent implements OnInit {
showModal: boolean = false;
faUpload = faUpload;
#Input() datasource: {
_id: string;
name: string;
description: string;
imagePath: string;
};
#Input() searchPlaceHolder1: string;
#Input() searchPlaceHolder2: string;
isModalActive: boolean = false;
#Output() messageEvent = new EventEmitter<string>();
select: string = 'not selected yet';
searchText: string = '';
fileArr = [];
sheetArr = [];
fileObj = [];
form: FormGroup;
msg: string;
progress: number = 0;
isButtonVisible: boolean = true;
constructor(
public fb: FormBuilder,
private sanitizer: DomSanitizer,
public dragdropService: DragdropService
) {
this.form = this.fb.group({
txt: [null],
});
}
ngOnInit(): void {}
onSelect() {
this.select = 'selected';
}
sendMessage() {
this.messageEvent.emit(this.datasource.name);
}
upload(e) {
const fileListAsArray = Array.from(e);
fileListAsArray.forEach((item, i) => {
const file = e as HTMLInputElement;
const url = URL.createObjectURL(file[i]);
this.sheetArr.push(url);
this.fileArr.push({ item, url: url });
});
this.fileArr.forEach((item) => {
this.fileObj.push(item.item);
});
// Set files form control
this.form.patchValue({
txt: this.fileObj,
});
this.form.get('txt').updateValueAndValidity();
// Upload to server
this.dragdropService
.addFiles(this.form.value.txt)
.subscribe((event: HttpEvent<any>) => {
switch (event.type) {
case HttpEventType.Sent:
console.log('Request has been made!');
break;
case HttpEventType.ResponseHeader:
console.log('Response header has been received!');
break;
case HttpEventType.UploadProgress:
this.progress = Math.round((event.loaded / event.total) * 100);
console.log(`Uploaded! ${this.progress}%`);
break;
case HttpEventType.Response:
console.log('File uploaded successfully!', event.body);
setTimeout(() => {
this.progress = 0;
this.fileArr = [];
this.fileObj = [];
this.msg = 'File uploaded successfully!';
}, 3000);
}
});
}
// Clean Url
sanitize(url: string) {
return this.sanitizer.bypassSecurityTrustUrl(url);
}
loading = { 1: false, 2: false, 3: false, 4: false };
doSomething(i: number) {
console.log('Clicked');
this.loading[i] = true;
setTimeout(() => {
this.loading[i] = false;
}, 2000);
}
selectItem() {
this.showModal = true;
}
}
drag-drop.service.ts
#Injectable({
providedIn: 'root',
})
export class DragdropService {
constructor(private http: HttpClient) {}
addFiles(sheets: File) {
var arr = [];
var formData = new FormData();
arr.push(sheets);
arr[0].forEach((item, i) => {
formData.append('txt', arr[0][i]);
});
return this.http
.post('http://localhost:4000/upload-file', formData, {
reportProgress: true,
observe: 'events',
})
.pipe(catchError(this.errorMgmt));
}
errorMgmt(error: HttpErrorResponse) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Get client-side error
errorMessage = error.error.message;
} else {
// Get server-side error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(errorMessage);
}
}
As for the back end code :
app.post("/upload-file", uploads.single("txt"), (req, res) => {
//convert csvfile to jsonArray
if (
req.file.mimetype === "application/vnd.ms-excel" ||
req.file.mimetype === "application/csv" ||
req.file.mimetype === "text / csv"
) {
const fileName = req.file.originalname;
csv({
delimiter: ";",
})
.fromFile(req.file.path)
.then((jsonObj) => {
//insertmany is used to save bulk data in database.
//saving the data in collection(table)
//finding the document using fileName and setting csvData as the jsonObj
sheetModel.findOneAndUpdate(
{ fileName: fileName },
{ $set: { csvData: jsonObj } },
{ upsert: true }, // if name does not exist insert
(err, csvData) => {
if (err) {
res.status(400).json({
message: "Something went wrong!",
});
} else {
res.status(200).json({
message: "File Uploaded Successfully!",
result: csvData,
});
}
}
);
});
}
Just add the additional fields to formData in the same way that you add the files:
formData.append('sourceId', sourceId);
It seems that you are using Multer middleware on the server. According to the documentation, "req.body will hold the text fields, if there were any".

How to create a document within a collection, with the same name as the unique authorized User UID?

I wrote a component that allows registration for the user.
The user enters an email, password, name and phone number.
Entering the email and password will enable registration (I used auth.service and registerWithEmail). After registration new user with a unique User UID will be created on the Authentication page in firebase:
I would like to create a situation where the details "name" and "phone" that the user entered, will be saved in a document with the same name as the User UID, in a collection called "user-info".
My Problem: The name of the document created is different from the unique User UID name.
In other words: I want the id marked in green in the image to be the id marked in red
The relevant code from crud.service.ts:
create_userInfo(RecordUserInfo)
{
return this.fireservices.collection('users').doc(this.authservice.currentUserId).collection('user-info').add(RecordUserInfo);
}
The relevant code from register.component.ts:
export class RegisterComponent implements OnInit {
user: any;
email="";
password="";
name="";
phone="";
message = '';
errorMessage = ''; //validation error handle
error: {name:string, message:string} = {name:'' , message:''}; //firebase error handle
constructor(private authservice: AuthService, private router: Router, public crudservice:CrudService) { }
ngOnInit(){
}
CreateRecordUserInfo()
{
if(this.authservice.currentUser != null)//We will make sure the user is logged in
{
let RecordUserInfo = {};
RecordUserInfo['name'] = this.name;
RecordUserInfo['email'] = this.email;
RecordUserInfo['phone'] = this.phone;
this.crudservice.create_userInfo(RecordUserInfo).then(res => {
this.name = "";
this.email = "";
this.phone = "";
this.message = "user-info data save done";
}).catch(error => {
console.log(error);
})
}
}
register()
{
this.clearErrorMessage();
if(this.validateForm(this.email, this.password, this.name, this.phone))
{
this.authservice.registerWithEmail(this.email, this.password)
.then(() => {
//we will save the user-info in collection named 'user-info'
this.CreateRecordUserInfo();
this.message = "Your data is registered in firebase"
this.router.navigate(['/home-page'])
}).catch(_error =>{
this.error = _error
this.router.navigate(['/register'])
})
}
}
The relevant code from auth.service.ts:
export class AuthService {
authState: any =null;
constructor(private afu: AngularFireAuth, private router: Router) {
this.afu.authState.subscribe((auth =>{
this.authState = auth;
}))
}
//function in use in register.component.ts
registerWithEmail(email: string, password: string){
return this.afu.createUserWithEmailAndPassword(email, password)
.then((user) => {
this.authState = user
}).catch(error=>{
console.log(error)
throw error
})
}
//get fanctions, to get data from firebase
get isUserAnonymousLoggedIn(): boolean{
return (this.authState !== null) ? this.authState.isAnonymous : false
}
get currentUserId(): string{
return (this.authState !== null) ? this.authState.uid : ''
}
get currentUserName(): string{
return this.authState['email']
}
get currentUser(): any{
return (this.authState !== null) ? this.authState : null;
}
get isUserEmailLoggedIn(): boolean{
if((this.authState !== null) && (!this.isUserAnonymousLoggedIn)){
return true
} else{
return false
}
}
My guess is that I call the function this.CreateRecordUserInfo(); in a problematic place, so that the registration itself is not finished yet. Do you have an idea how to solve the problem?
many thanks!
Please try this.
registerWithEmail(email: string, password: string){
return new Promise(resolve => {
this.afu.createUserWithEmailAndPassword(email, password)
.then((credential) => {
this.authState = credential.user;
resolve(credential.user);
}).catch(error=>{
console.log(error)
throw error;
})
});
}

call material dialog as alert from service.ts file

I am using angular material. I am handling common error response in the handleError method in my service.ts file. I want to popup material dialog when I get an error instead of alert from service.ts file.
How can I implement this?
I am new to the angular material.
Code:
export class CommonService {
public api = 'https://URl'
public showSpinner: boolean = false;
public auth: boolean = false;
public fetch: boolean = false;
constructor(public http: Http) { }
postCall() {
this.showSpinner = false;
this.auth = false;
this.fetch = false;
var header = {
"headers": {
"content-type": "application/x-www-form-urlencoded",
}, "grant_type": "password",
"scope": "user",
"client_id": "4874eafd0f7a240625e59b2b123a142a669923d5b0d31ae8743f6780a95187f5",
"client_secret": "908f6aee4d4cb27782ba55ae0c814bf43419f3220d696206212a29fe3a05cd88",
"auth_token": "azd4jXWWLagyb9KzgfDJ"
};
return this.http.post(this.api + '/oauth/token.json', header)
.map(response => {
this.showSpinner = true;
this.auth = true;
this.fetch = false;
setTimeout(function () {
let result = response.json();
window.localStorage.setItem('access_token', result.access_token);
}, 4000);
return true;
})
.catch(this.handleError)
}
getCaseStudy() {
this.showSpinner = true;
this.auth = false;
this.fetch = true;
let headers = new Headers();
let token = window.localStorage.getItem('access_token');
headers.append('Authorization', 'Bearer ' + token);
headers.append('content-type', 'application/json');
let Hdata = new RequestOptions({ headers: headers })
return this.http.get(this.api + '/upend URl', Hdata)
.map(response => {
this.showSpinner = false;
this.fetch = false;
this.auth = false;
return response.json()
})
.catch(this.handleError);
}
private handleError() {
return Observable.throw(
alert('problem somewhere')
)
}
}
Thanks in advance.
You can create a component ErrorDialog that would be at root level. In AppComponent subscribe a subject declared in CommonService that will provide boolean value.
In CommonService you can do this as:
private subject = new Subject<any>();
updateDialog(isVisible: boolean) {
this.subject.next({ isVisible: isVisible });
}
getDialogVisibility(): Observable<any> {
return this.subject.asObservable();
}
handleError(error: any) {
...
this.updateDialog(true);
...
}
In your component subscribe getDialogVisibility and whenever value is being changed from service you can get to know if dialog should be displayed.
AppComponent
#Component({
selector: 'app-root',
template:`
<router-outlet></router-outlet>
<error-dialog></error-dialog>
`
})
export class AppComponent implements OnDestroy {
subscription: Subscription;
constructor(private commonService: CommonService) {
this.subscription = this.commonService.getDialogVisibility().subscribe(isVisible => {
if(isVisible) {
openErrorDialog();
}
});
}
ngOnDestroy() {
// unsubscribe to ensure no memory leaks
this.subscription.unsubscribe();
}
openErrorDialog() {
// write your code
}
}

Nested Observables behaving differently in Ionic2/Angular2 App

I am creating an ionic login module, where in there are 2 observables , 1 inside another, Not sure if this is the correct way of implementation,
Here I am trying to call getHTTP() method, get a string, if the string is not empty then set it in ionic-storage varaible and then verify before logging in
Since Observables are async - getHTTP() is getting completed after the flow of login(credentials) , Help me out
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
import {Observable} from 'rxjs/Observable';
import {Headers} from '#angular/http';
import { Response } from '#angular/http';
import { Storage } from '#ionic/storage';
export class User {
name: string;
password: string;
url: string;
constructor(name: string, password: string, url: string) {
this.name = name;
this.password = password;
this.url = url;
}
}
/*
Generated class for the AuthService provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
#Injectable()
export class AuthService {
currentUser: User;
data = '';
constructor(public http: Http,private storage: Storage) {
console.log('Hello AuthService Provider');
}
// Make a call to Get CSRF and check if we have access
public getHTTP(credentials) {
let responseCSRF ;
const headers: Headers = new Headers();
headers.append('Authorization', 'Basic ' + btoa(credentials.user + ':' + credentials.password));
headers.append('Content-Type', 'application/json');
console.log(headers);
console.log('Clearing cache');
this.storage.set('CSRF', '');
this.storage.set('Auth',btoa(credentials.user + ':' + credentials.password));
this.storage.set('url', credentials.url);
//return
responseCSRF = this.http.get('http://' + credentials.url +'/Windchill/servlet/rest/security/csrf', {
headers: headers
}).map((response: Response) => response.json());
//console.log(typeof(responseCSRF))
responseCSRF.subscribe(x => {
console.log('CSRF ->' + x.items[0].attributes.nonce)
this.data = x.items[0].attributes.nonce;
if(typeof this.data!='undefined' && this.data) {
this.storage.set('CSRF', this.data);
}
});
return responseCSRF;
}
public login(credentials) {
if (credentials.user === null || credentials.password === null || credentials.url === null ) {
return Observable.throw("Please insert credentials ");
} else {
return Observable.create(observer => {
// At this point make a request to your backend to make a real check!
let access = false;
this.getHTTP(credentials).subscribe (
(resBody) => console.log('Boby is '+resBody),
error => console.error('Error from auth-service: ' + error))
, () => console.log('Completed!' + 'Auth' );
this.storage.get('CSRF').then((val) => {
console.log('Your CSRF is'+ val);
if(val!='undefined') {
access = true;
}
});
observer.next(access);
observer.complete();
});
}
}
public getUserInfo() : User {
return this.currentUser;
}
public logout() {
return Observable.create(observer => {
this.currentUser = null;
observer.next(true);
observer.complete();
});
}
}
In the Console
Headers {_headers: Map(2), _normalizedNames: Map(2)}
auth-service.ts:49 Clearing cache
auth-service.ts:57 pluck -->[object Object]
auth-service.ts:83 Your CSRF is
auth-service.ts:59 CSRF ->RkPYp+UtGGMRB+8NJHCr9rJ6WhBHdIVCfim585xXKgZ1TKUmf3v39tBqVRkjSb93dgWi4oF3KF4rNts0c3frktUdIFokNNVrMSGM47V3KwQhP8A5ARKr5rBsaxtmOtI=
auth-service.ts:78 Boby is [object Object]
Try to put your storage.get logic inside subscription handler:
return Observable.create(observer => {
// At this point make a request to your backend to make a real check!
let access = false;
this.getHTTP(credentials).subscribe(
(resBody) => {
console.log('Boby is ' + resBody);
this.storage.get('CSRF').then((val) => {
console.log('Your CSRF is' + val);
if (val != 'undefined') {
access = true;
}
observer.next(access);
observer.complete();
});
},
error => console.error('Error from auth-service: ' + error),
() => console.log('Completed!' + 'Auth'));
});

How to use angular 2 service which returns http promise

I have a trouble with angular 2 here.
I use service that return promise but when i try to retrive the response i got an error.
i was read this this stact question
this my code.
this is HotelService.ts
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
//rxjs promises cause angular http return observable natively.
import 'rxjs/add/operator/toPromise';
#Injectable()
export class HotelService {
private BASEURL : any = 'http://localhost:8080/hotel/';
constructor(private http: Http) {}
load(): Promise<any> {
return this.http.get(this.BASEURL + 'api/client/hotel/load')
.toPromise()
.then(response => {
response.json();
//console.log(response.json());
})
.catch(err => err);
}
}
this Hotel.ts (component)
import { Component, OnInit } from '#angular/core';
import { NavController } from 'ionic-angular';
import { HotelService } from '../../providers/hotel/hotelservice';
import { AboutPage } from '../../pages/about/about';
import { HotelDetailPage } from '../../pages/hoteldetail/hotel';
#Component({
selector: 'page-home',
templateUrl: 'home.html',
providers: [HotelService]
})
export class HomePage implements OnInit {
public searchBoxActive = false;
public hotels: any;
constructor(
private navCtrl: NavController,
private hotelServ: HotelService
) { }
load() {
this.hotelServ.load()
.then(res => {
this.hotels = res;
console.log(res); //why the rest is undefined?
console.log('ini component');
},
err => err);
}
toggleSearchBox() {
if (this.searchBoxActive == false) {
this.searchBoxActive = true;
} else {
this.searchBoxActive = false;
}
}
showAbout() {
this.navCtrl.setRoot(AboutPage);
}
pushDetail(evt, id) {
this.navCtrl.push(HotelDetailPage)
}
ngOnInit(): void {
this.load();
}
}
I have no idea.
You need to return response.json() from promise then callback:
load(): Promise<any> {
return this.http.get(this.BASEURL + 'api/client/hotel/load')
.toPromise()
.then(response => {
return response.json();
})
.catch(err => err);
}
The dfsq's answer is correct, but for the completeness' sake, below is an example according to the official Angular.io recommendations:
load(): Promise<any> {
return this.http.get(this.BASEURL + 'api/client/hotel/load')
.toPromise()
.then(response: Response) => response.json() || {})
.catch((error: Response | any) =>
{
let errMsg: string;
if (error instanceof Response)
{
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
}
else
errMsg = error.message ? error.message : error.toString();
return Promise.reject(errMsg);
});
}
Key differences:
handle empty response in the then;
pretty up the error before throwing it further.

Categories

Resources