I trying to get data when I log in, by sending ID from localStorage. Everything I tried didn't work, and the only thing comes to my mind is that getting ID from local storage works synchronously. I hope someone can help me make it async. Unfortunately, I don't have permission to show API here. The code:
auth.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '#angular/common/http';
import { throwError, Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { Restaurant } from '../models/Restaurant';
import { LocalStorage } from '#ngx-pwa/local-storage';
#Injectable({
providedIn: 'root'
})
export class AuthService {
loginUrl = 'xxxxxxxxxx';
errorData: {};
constructor(private http: HttpClient) { }
redirectUrl: string;
login(email: string, password: string) {
var postData = {email: email, password: password};
return this.http.post<Restaurant>(this.loginUrl, postData)
.pipe(map(restaurant => {
if (restaurant) {
localStorage.setItem('currentRestaurant', JSON.stringify(restaurant));
return restaurant;
}
}),
catchError(this.handleError)
);
}
isLoggedIn() {
if (localStorage.getItem('currentRestaurant')) {
return true;
}
return false;
}
getAuthorizationToken() {
const currentRestaurant = JSON.parse(localStorage.getItem('currentRestaurant'));
return currentRestaurant.token;
}
logout() {
localStorage.removeItem('currentRestaurant');
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong.
console.error(`Backend returned code ${error.status}, ` + `body was: ${error.error}`);
}
// return an observable with a user-facing error message
this.errorData = {
errorTitle: 'Oops! Request for document failed',
errorDesc: 'Something bad happened. Please try again later.'
};
return throwError(this.errorData);
}
currRestaurant: Restaurant = JSON.parse(localStorage.getItem('currentRestaurant'));
currID = this. currRestaurant.id;
}
login.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, Validators, FormGroup } from '#angular/forms';
import { Router } from '#angular/router';
import { AuthService } from '../services/auth.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
loginForm: FormGroup;
submitted = false;
returnUrl: string;
error: {};
loginError: string;
constructor(
private fb: FormBuilder,
private router: Router,
private authService: AuthService
) { }
ngOnInit() {
this.loginForm = this.fb.group({
email: ['', Validators.required],
password: ['', Validators.required]
});
this.authService.logout();
}
get email() { return this.loginForm.get('email'); }
get password() { return this.loginForm.get('password'); }
onSubmit() {
this.submitted = true;
this.authService.login( this.email.value, this.password.value).subscribe((data) => {
if (this.authService.isLoggedIn) {
const redirect = this.authService.redirectUrl ? this.authService.redirectUrl : '/';
this.router.navigate([redirect]);
} else {
this.loginError = 'email or password is incorrect.';
}
},
error => this.error = error
);
}
}
Thanks everyone for their time
There are some mistakes:
Are you aware that you use Native localStorage, not the one you import - import { LocalStorage } from '#ngx-pwa/local-storage'; (and also it should be injected in the constructor if you want to use it, and used in asynchronous way)
if (this.authService.isLoggedIn) { will always be true, because this.authService.isLoggedIn is a function and it is not a falsy value. You probably want to execute it - if (this.authService.isLoggedIn()) {
redirectUrl is always undefined because your provided snippets does not assign it any value.
Related
I'm working on authentication the project works fine and connect to the database in the firebase, because the project had parts when I need to retrieve data from it, when I try to login with right email and password I get the current error:
Error: A network error (such as timeout, interrupted connection or unreachable host) has occurred.
the loginAdminService:
import { Injectable } from '#angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { AngularFireAuth } from "#angular/fire/auth";
import * as fireBase from 'firebase';
#Injectable({
providedIn: 'root'
})
export class LoginAdminserviceService {
isAuth: boolean;
constructor(private angularFireAuth: AngularFireAuth,loginDatabase: AngularFireDatabase) {
}
async login(email: string, password: string) {
return new Promise(
(resolve,reject)=>{
fireBase.auth().signInWithEmailAndPassword(email,password).then(
()=>{
this.isAuth = true;
resolve();
},
(error)=>{
this.isAuth = false;
reject(error);
}
)
}
)
}
async logout() {
return await this.angularFireAuth.auth.signOut();
}
isUserLoggedIn() {
return JSON.parse(localStorage.getItem('user'));
}
}
the authentication component:
import { Component, OnInit } from '#angular/core';
import { LoginAdminserviceService } from '../services/login-adminservice.service';
import { Route, Router } from '#angular/router';
#Component({
selector: 'app-authentication',
templateUrl: './authentication.component.html',
styleUrls: ['./authentication.component.css']
})
export class AuthenticationComponent implements OnInit {
constructor(private route:Router , public loginServiceasAdmin : LoginAdminserviceService) { }
ngOnInit() {
}
async loginAdmin(email:string,password:string){
this.loginServiceasAdmin.login(email,password).then(
()=>{
alert('Bienvenue '+email);
this.route.navigate(['/listreclamation']);
},
(error)=>{
console.log('Pas de connexion '+error);
alert('Votre compte est incorrect');
});
}
}
the html page:
<form>
Email:<input type="text" #email><br>
Password:<input type="password" #password><br>
<button type="submit" (click)="loginAdmin(email.value,password.value)">Login as Admin</button>
<button type="submit" (click)="this.loginServiceasAdmin.logout()">Logout</button>
</form>
Simple example how login service should looks like:
export class AuthService {
user$: Observable<firebase.User>
constructor(private afAuth: AngularFireAuth) {
this.user$ = this.syncUser()
}
// function trigered once are listen when user is logged in.
syncUser() {
return this.afAuth.authState.pipe(
switchMap(user => {
if(user){
return of(user)
} else {
return of(null)
}
})
)
}
// return is not necesery you can allways listen in real time user$ variable.
async signInWith(credentials: IdCredentials) {
const result = await this.afAuth.auth.signInWithEmailAndPassword(credentials.email, credentials.password)
return result
}
}
Make sure you that in your module fire your use emulators Url is as follows:
['http://localhost:<portnumber>', <portnumber>]
and not
['http://localhost', <portnumber>]
I call service which make http call, I assign response to component variable now when I try access that component variable to view it display blank.
Means component variable assign in subscribe successfully but cant acceess in html view.
I think view is loaded before values assign to component data.
component
import {Component, OnInit, ChangeDetectionStrategy} from '#angular/core';
import { UserService } from '../../../../../core/services/users/user.service';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'm-user-list',
templateUrl: './user-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserListComponent implements OnInit {
list;
roles = {};
current_page: any
totalRecords: any
public showContent: boolean = false;
constructor(private userService: UserService, private http: HttpClient) {
}
ngOnInit() {
this.getRecords();
}
getRecords(){
this.getResultedPage(1);
}
getResultedPage(page){
return this.userService.getrecords()
.subscribe(response => {
this.list = response.data;
});
}
}
Service
import { Injectable } from '#angular/core';
import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpParams , HttpErrorResponse, HttpHeaders } from '#angular/common/http';
import { map, catchError, tap, switchMap } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
import { UtilsService } from '../../services/utils.service';
import { AppConfig } from '../../../config/app'
#Injectable({
providedIn: 'root'
})
export class UserService{
public appConfig: AppConfig;
public API_URL;
constructor(private http: HttpClient, private util: UtilsService) {
this.appConfig = new AppConfig();
this.API_URL = this.appConfig.config.api_url;
}
private extractData(res: Response) {
let body = res;
return body || { };
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError('Something bad happened; please try again later.');
};
getrecords(): Observable<any> {
return this.http.get('/api/users', httpOptions).pipe(
map(this.extractData),
catchError(this.handleError));
}
}
I am new to Angular and having hard time grasping how to deal with async requests.
I have 3 components:
parent - AppComponent
children - LoginComponent,NavbarComponent,DashboardComponent,MainComponent,SidebarComponent
and one AuthService
On pressing "Login" button at the logging component I need to transfer boolean "true" value to all components.
On pressing "Logout" button at the navbar component I need to transfer boolean "false" value to all components and set user=null
if true ->
set token in localStorage with user ID
preform http.get("http://localhost:3000/user/"+id) request to retrieve full user info and inject user info to Dashboard,Main,Sidebar,App and Navbar components.
The problem is that whenever I logout the false/true value updates on all components immediately but the user info does not turn into null unless I refresh the page or send it with router to another component and then return to MainComponent, same thing with new login.
How do I update both user info and status in all components immediately without refreshing the page?
authService:
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { Router } from "#angular/router";
import { User } from "../models/User";
#Injectable({ providedIn: "root" })
export class AuthService {
user: User;
private _url = "http://localhost:5000/user/";
constructor(private http: HttpClient, private _router: Router) {}
registerUser(user) {
return this.http.post<any>(this._url + "register", user);
}
loginUser(user) {
return this.http.post<any>(this._url + "login", user);
}
logoutUser() {
localStorage.clear();
this._router.navigate(["/"]);
}
loggedIn() {
return !!localStorage.getItem("token");
}
getToken() {
return localStorage.getItem("token");
}
getCurrentUser() {
return this.http.get<any>("http://localhost:5000/shop/current");
}
}
Main/Sidebar component:
import { Component, OnInit, DoCheck } from "#angular/core";
import { AuthService } from "src/app/services/auth.service";
import { User } from "src/app/models/User";
#Component({
selector: "app-sidebar",
templateUrl: "./sidebar.component.html",
styleUrls: ["./sidebar.component.css"]
})
export class SidebarComponent implements OnInit, DoCheck {
isSidenavOpen: boolean = true;
user: User;
constructor(private _authService: AuthService) {}
ngOnInit() {
if (this._authService.loggedIn()) this._authService.getCurrentUser().subscribe(res => (this.user = res.user));
else this.user = null;
}
ngDoCheck() {
if (!this._authService.loggedIn()) this.user = null;
}
}
login:
constructor(private _authService: AuthService, private _router: Router) {}
// onLoginUser() {
// this._authService.loginUser(this.loginUserData).subscribe(
// res => {
// localStorage.setItem("token", res.token);
// localStorage.setItem("user", res.user._id);
// this._router.navigate(["/"]);
// },
// err => console.log(err)
// );
// }
}
Use event EventEmitter to emit event on login, logout etc and listen in each component, that depends on user data.
In service, where you call logoutUser, loginUser methods of AuthService:
// LoginService
userLoggin: EventEmitter<User> = new EventEmitter();
userLoggout: EventEmitter<any> = new EventEmitter();
constructor(private _authService: AuthService, private _router:
Router) {}
loginUser() {
this._authService.loginUser(this.loginUserData).subscribe(
res => {
localStorage.setItem("token", res.token);
localStorage.setItem("user", res.user._id);
this.userLoggin.emit(res.user);
this._router.navigate(["/"]);
},
err => console.log(err)
);
}
logoutUser() {
this._authService.logoutUser().subscribe(
res => {
this.userLoggout.emit();
},
err => console.log(err)
);
}
}
Then in component:
import { Component, OnInit, DoCheck } from "#angular/core";
import { AuthService } from "src/app/services/auth.service";
import { User } from "src/app/models/User";
import { LoginService } from "src/app/services/login.service";
#Component({
selector: "app-sidebar",
templateUrl: "./sidebar.component.html",
styleUrls: ["./sidebar.component.css"]
})
export class SidebarComponent implements OnInit, DoCheck {
isSidenavOpen: boolean = true;
user: User;
loggin$: Subscription;
logout$: Subscription;
constructor(private _authService: AuthService, private _loginService: LoginService) {
this.loggin$ = this._loginService.userLoggin.subscribe(user => {
this.user = user;
});
this.logout$ = this._loginService.userLoggout.subscribe(user => {
this.user = null;
});
}
ngOnInit() {
}
ngOnDestroy() {
this.loggin$.unsubscribe();
this.logout$.unsubscribe();
}
}
Im trying a simple profile app, and all the sudden Im getting error TS2554
ERROR in /app/components/profile/profile.component.ts(25,3): error TS2554: Expected 1 arguments, but got 0.
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { FlashMessagesService } from 'angular2-flash-messages';
import { Router } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
user: Object;
constructor(
private auth: AuthService,
private flashMsg: FlashMessagesService,
private router: Router
) {
}
ngOnInit() {
this.auth.getProfile().subscribe( profile => {
this.user = profile.user;
},
err => {
return false;
});
}
}
auth.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/add/operator/map';
import { tokenNotExpired } from 'angular2-jwt';
#Injectable()
export class AuthService {
authToken: any;
user: any;
constructor(
private http: Http
) {
}
getProfile(user) {
let headers = new Headers();
this.loadToken();
headers.append('Authorization', this.authToken);
headers.append('Content-Type','application/json');
return this.http.get('http://localhost:3000/users/profile', {headers:headers})
.map(res => res.json());
}
loadToken() {
const token = localStorage.getItem('id_token');
this.authToken = token;
}
}
Your getProfile is expecting an argument named user but you are not passing it from the component
You need to pass an argument as follows,
this.auth.getProfile(user).subscribe( profile => {
this.user = profile.user;
},
err => {
return false;
});
or if you don't need an argument , remove it from your service method.
I've got a sign up form and I want to check that if the username is already taken or not. To achieve this I'm using promises now.
My sign up component looks like this:
import { Component, OnInit } from '#angular/core';
import { FormControl, FormBuilder, FormGroup, Validators } from '#angular/forms';
import { UserService } from '../shared/user.service'
#Component({
selector: 'app-auth',
providers: [],
templateUrl: './auth.component.html',
styleUrls: ['./auth.component.css']
})
export class AuthComponent implements OnInit {
// Tabs for log in or sign up
tab = 'signup';
// Sign up form
signUpForm: FormGroup;
// Log in form
logInForm: FormGroup;
constructor(private formBuilder: FormBuilder, private ussr: UserService) {
this.signUpForm = this.formBuilder.group({
'username': [null, [
Validators.required, Validators.minLength(4), Validators.maxLength(12), this.ussr.getUserNameFromServer
]],
'email': '',
'password': '' });
this.logInForm = this.formBuilder.group({ 'username': '', 'password': '' });
}
ngOnInit() {
}
activeTab(tab: string) {
this.tab = tab;
}
signUpSubmit(value: any) {
console.log(value);
}
}
And the UserService:
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { FormControl } from '#angular/forms';
import 'rxjs/add/operator/map';
#Injectable()
export class UserService {
constructor (private http: Http) {}
private extractData (res: Response) {
let body = res.json();
return body || { };
}
getUserNameFromServer = (c: FormControl) => {
return new Promise (
(resolve, reject) => {
this.http.get('https://jsonplaceholder.typicode.com/users/1')
.map(this.extractData)
.subscribe(
(res: any) => {
if (c.value == res.username) {
console.log('taken')
resolve({'usernameTaken': true})
} else {
console.log('is not taken')
resolve(null)
}
},
err => { console.log(err) }
)
}
);
}
}
I already read some blog posts about this topic, and I also checked two SO questions (1, 2), but I can't get it to work.
The service successfully got the server's answer, but when I call it inside the component's validator, the form is going to be invalid every time.
In the examples above, they just call it in the validator section, and I guess the ng2 do the rest of the work in the background, or am I wrong? How is the validator got the promise's value?
The problem was that I insert the async custom validator into the sync validator section. Which means the 2nd param is for the sync validators, the 3rd one for the async ones.
This works now:
'username': [null,
[ Validators.required, Validators.minLength(4), Validators.maxLength(12) ],
[ this.ussr.getUserNameFromServer ]
],