In the template component AppComponent, depending on the value, the variable this.loggedInService.isLoggedIn switches between the logIn() and logout() methods, which in the application component AppComponent are subscribed to these methods in the service LoggedinService and depending on the method, change the value of the variable to true or false.
Also in the Guard's method checkLogin (url: string) I return true or false depending on the value of the variable this.loggedInService.isLoggedIn
Everything works, but when I reset the page, I need to keep the value of the input or output button. I try to do this in the login() and logout() methods in the service, but after reloading the page, the changes are still not saved. Help solve this problem so that the changes remain after the page reboot.
template of AppComponent:
<li class="nav-item">
<a class="btn btn-outline-success"
[class.btn-outline-success]="!this.loggedInService.isLoggedIn"
[class.btn-outline-danger]="this.loggedInService.isLoggedIn"
(click)="this.loggedInService.isLoggedIn ? logout() : logIn()">
{{this.loggedInService.isLoggedIn ? 'Exit' : 'Enter'}}
</a>
</li>
code of AppComponent:
export class AppComponent implements OnInit {
constructor(private loggedInService: LoggedinService,
private router: Router) {
}
ngOnInit() {}
logIn(): void {
this.loggedInService.login();
if (this.loggedInService.isLoggedIn) {
let redirect = this.loggedInService.redirectUrl ? this.loggedInService.redirectUrl :
'/gallery';
this.router.navigate([redirect]);
}
}
logout(): void {
this.loggedInService.logout();
this.router.navigate(['/']);
}
}
LoggedinService:
export class LoggedinService implements OnInit {
isLoggedIn: boolean = false;
redirectUrl: string;
constructor() {
}
ngOnInit() {
this.CheckAuthentication();
}
enter code here
CheckAuthentication(): boolean {
if (localStorage.getItem('login') === 'true') {
return this.isLoggedIn = true;
} else if (localStorage.getItem('login') === 'false') {
return this.isLoggedIn = false;
}
}
login() {
localStorage.setItem('login', 'true')
}
logout() {
localStorage.removeItem('login');
localStorage.setItem('login', 'false')
}
AuthGuard:
export class AuthGuard implements CanActivate {
constructor(private loggedInService: LoggedinService) {
}
canActivate(next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean{
let url: string = state.url;
return this.checkLogin(url);
}
checkLogin(url: string): boolean {
if (this.loggedInService.isLoggedIn) {
return true;
} else {
this.loggedInService.redirectUrl = url;
return false;
}
}
}
Change is isLoggedIn to be get method base on localStorage item
export class LoggedinService implements OnInit {
redirectUrl: string;
constructor() {}
get isLoggedIn(): boolean {
return localStorage.getItem('login') ? true : false;
}
login(){
localStorage.setItem('login','true')
}
logout(){
localStorage.removeItem('login')
}
}
app.component
export class AppComponent {
constructor(private loggedInService: LoggedinService,
private router: Router) {
}
logIn(): void {
this.loggedInService.login(); // set the state as login
let redirect = this.loggedInService.redirectUrl ? this.loggedInService.redirectUrl :
'/gallery';
this.router.navigate([redirect]);
}
logout(): void {
this.loggedInService.logout(); //// set the state as logout
this.router.navigate(['/']);
}
}
stackblitz demo
I have a doubt with your code.
In LoggedInService onInit why are you calling login() and logout() directly?
this.CheckAuthentication();
this.login();
this.logout();
Doing that is adding and deleting from your localStorage. Also, you can check data in your local storage by typing localStorage in browser console.I think you should comment or remove onInit method
Related
I am trying to make a modal in Angular 9 that returns a Promise as result. I don't know how to move the promise logic outside of the declaration.
<a class="button-primary" (click)="yes()">Yes</a>
<a class="button-default" (click)="no()">No</a>
This is the modal controller
import { Component, OnInit, HostBinding } from '#angular/core';
#Component({
selector: 'change-username-modal',
templateUrl: './change-username-modal.component.html',
styleUrls: ['./change-username-modal.component.less']
})
export class ChangeUsernameModalComponent implements OnInit {
#HostBinding('class.show')
show: boolean = false;
constructor() { }
ngOnInit(): void {
console.log('init');
}
public open(): Promise<boolean> {
return new Promise(function(resolve, reject) {
resolve(true);
});
}
yes() {
//this.myPromise.resolve(true);
this.show = false;
}
no() {
//this.myPromise.reject(false);
this.show = false;
}
}
I need to make the Promise resolve or reject when calling the yes() or no() functions.
Thank you in advance!
You could use Observable approach instead of promise. you need a simple subject which will emit and complete immediately (for avoiding memory leak). the code should look like this
export class Component{
#HostBinding('class.show')
show: boolean = false;
private _emitter$ = new Subject<boolean>();
constructor() { }
ngOnInit(): void {
console.log('init');
}
public open(): Observable<boolean> {
return this._emitter.asObservable();
}
yes() {
//this.myPromise.resolve(true);
this.show = false;
this.emitAndClose(true);
}
emitAndClose(answer:boolean){
this._emitter.next(answer);
this._emitter.complete();
}
no() {
this.emitAndClose(false);
this.show = false;
}
}
now whenever answer is clicked, it will emit the value and complete the subject so you don't need unsubscribe outside
<app-example [id]="requestId"></app-example>
We have add the selector in another component with passing the id.
export class exampleComponent implements OnInit {
#Input() id:number;
Id:string;
}
constructor(private fb: FormBuilder, private _AppService: AppService, private router: Router, private route: ActivatedRoute) {
this.router.getCurrentNavigation().extras.state != undefined ? this.Id = this.router.getCurrentNavigation().extras.state.key : '';
this.route.data.subscribe(data => {
this.current_path = data.urlname;
})
}
we've using the getCurrentNavigation() method,
but its return null value
Try something like this
constructor(private router: Router) {
router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {
this.current_path = event.url;
}
})
I'm new to Firebase, and I'm trying to implement authentication from an angular 7 application.
Here is my Authentication service:
#Injectable({
providedIn: 'root'
})
export class AuthService {
private user: Observable<firebase.User>;
private userDetails: firebase.User;
constructor(private angularFireAuth: AngularFireAuth) {
this.user = this.angularFireAuth.user;
this.user.subscribe(
(user) => {
if (user) {
this.userDetails = user;
}
else {
this.userDetails = null;
}
}
);
}
signInGoogleLogin() {
return this.angularFireAuth.auth.setPersistence(firebase.auth.Auth.Persistence.LOCAL)
.then(() =>
this.angularFireAuth.auth.signInWithPopup(
new firebase.auth.GoogleAuthProvider()
)
);
}
isLoggedIn(): boolean {
return this.userDetails != null;
}
}
And here is my AuthGuard implementation:
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService: AuthService) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<boolean> | Promise<boolean> | boolean {
if (this.authService.isLoggedIn()) {
return true;
}
this.router.navigate(['login'], { queryParams: { returnUrl: state.url}});
return false;
}
}
My problem is: the persistence does not seem to work. Whenever I refresh the page, I have to log in, whenever I'm navigating to another component that needs authentication, I need to log in again.
Of course, if I use "signInWithRedirect" instead of "signInWithPopup" I fall into a logging loop where I get redirected to my login page which finds that I'm not logged in, then try to log me, redirects me to my login page which finds I'm not logged in, and so on.
I think all these problems are actually related to the same problem: my auth state persistence implementation is somewhat wrong.
So my question is really simple: what am I doing wrong ? :)
I'd like to be able to log in, and then stay logged in when a refresh occurs.
Thank you for your help. :)
If anyone comes here looking for an answer this is how I did it
auth.service.ts
import { auth, firestore } from 'firebase/app';
constructor(
private _fAuth: AngularFireAuth,
) {}
public async login(authInfo: UserAuthInfo) {
if(authInfo.rememberMe) {
await this._fAuth.setPersistence(auth.Auth.Persistence.LOCAL)
console.log("local persistance", true);
}
const credential = await this._fAuth.signInWithEmailAndPassword(authInfo.username, authInfo.pass);
...
}
auth.guard.ts
export class AuthGuard implements CanActivate {
constructor(
private _fAuth: AngularFireAuth,
private _router: Router
) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> {
return this._authService.fAuth.authState.pipe(
first(),
map(user => !!user),
tap(authenticated => {
console.log("auth guard loggedin", authenticated);
authenticated || this._router.parseUrl('/auth/login')
})
)
}
}
Passing the URL id from the last page a user was on to a service that I can reference in a dialog.
issuer.service.ts
import { Injectable, EventEmitter } from '#angular/core';
import { Observable, of } from 'rxjs';
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class IssuerService {
private urlidSource = new BehaviorSubject<string>('');
currentUrlid = this.urlidSource.asObservable();
public onChange: EventEmitter<string> = new EventEmitter<string>();
constructor () {
}
changeUrlid(urlid: string) {
this.currentUrlid = of(urlid);
this.onChange.emit(urlid);
}
getUrlid(currentUrlid: string) {
return this.currentUrlid;
}
}
Page that has the URL id I want (dashboard.component.ts)
import { IssuerService } from './../../issuer.service';
import { ActivatedRoute } from '#angular/router';
import { Router } from '#angular/router';
urlid: string;
constructor(
private route: ActivatedRoute,
private router: Router,
private issuerService: IssuerService,
public dialog: MatDialog
) {}
newUrlid() {
this.issuerService.changeUrlid(this.route.snapshot.paramMap.get('id'));
console.log(this.urlid);
}
ngOnInit() {
// Get URL ID
this.issuerService.onChange.subscribe(urlid => this.urlid = urlid);
this.newUrlid();
}
Component I want to read the value in:
import { ActivatedRoute } from '#angular/router';
import { Router } from '#angular/router';
import { IssuerService } from './../../issuer.service';
urlid: string;
constructor(
private route: ActivatedRoute,
private router: Router,
private issuerService: IssuerService,
public dialog: MatDialog
) {}
ngOnInit() {
this.issuerService.onChange.subscribe(urlid => {
this.urlid = urlid;
console.log(this.urlid);
});
}
So currently when I visit my dashboard page it will display the value of 2 which is correct. My goal is that when a user visits any page I can read this value of 2. How can I access this value? The above code works and my Header displays 2 but only when on the dashboard page. I need it to display 2 no matter what page the user is on.
you can see this example, and It's modified list:
use queryPamas to get query string, not params (DashboardComponent)
use ReplaySubject(1) to return the last urlId; it's don't have a default value, just return prev one value (IssuerService)
get observable from getUrlid and subscribe it in components that want to show url id
export class IssuerService {
private urlidSource = new ReplaySubject<string>(1);
constructor() {
}
changeUrlid(urlid: string) {
this.urlidSource.next(urlid);
}
getUrlid() {
return this.urlidSource;
}
}
export class DashboardComponent implements OnInit {
urlid: string;
constructor(
// private route: ActivatedRoute,
private router: Router,
private issuerService: IssuerService,
// public dialog: MatDialog
) { }
newUrlid() {
// Get URL ID
this.route.queryParams.subscribe((queryParam) => {
const id = queryParam['id'];
if (!id) {
return;
}
this.issuerService.changeUrlid(id);
});
}
ngOnInit() {
this.newUrlid();
this.issuerService.getUrlid().subscribe(urlid => {
this.urlid = urlid;
});
}
}
export class HelloComponent implements OnInit {
urlid;
constructor(
private issuerService: IssuerService
) { }
ngOnInit() {
this.issuerService.getUrlid().subscribe(urlid => {
this.urlid = urlid;
});
}
}
You do not need a parameter for your get Method since you already have the value inside the service,
getUrlid() {
return this.currentUrlid;
}
and you can use retrieve the value in the 2nd component as follows,
this.issuerService.currentUrlid.subscribe((value: string) => {
this.urlid = value;
}
I have a bootstrap navbar, on the right side of navigation bar, i have some links like login,logout, register
I put it on my app.component.html.ts
<div class="navbar-collapse collapse">
// Here i check if user is authenticated, display : Hello abc#gmail.com
<ul *ngIf="user" class="nav navbar-nav navbar-right">
//code in here
</ul>
// If user is not authenticated, display Login - Register
<ul *ngIf="!user" class="nav navbar-nav navbar-right">
<li><a routerLink="/register" id="registerLink">Register</a></li>
<li><a routerLink="/login" id="loginLink">Log in</a></li>
</ul>
In login.component.ts i call my Authen.Service.ts to get token that is store on localStorage
import { UrlConstants } from './core/common/url.constants';
import { LoggedInUser } from './core/domain/loggedin.user';
import { SystemConstants } from './core/common/system.constants';
#Component({
selector: 'app-login',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
public user: any;
private isLoggedIn = false;
loginUser(valid: boolean) {
this.loading = true;
if (valid) {
const userData = {
username: this.form.controls.username.value,
password: this.form.controls.password.value
}
this._authenService.login(userData.username, userData.password).subscribe(data => {
this.user = JSON.parse(localStorage.getItem(SystemConstants.CURRENT_USER));
// If success redirect to Home view
this._router.navigate([UrlConstants.HOME]);
}, error => {
this.loading = false;
});
}
}
ngOnInit() {
}
}
Here is my Authen.Service.ts
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import 'rxjs/add/operator/map';
import { SystemConstants } from '../common/system.constants';
import { LoggedInUser } from '../domain/loggedin.user';
#Injectable()
export class AuthenService {
constructor(private _http: Http) {
}
login(username: string, password: string) {
let body = "userName=" + encodeURIComponent(username) +
"&password=" + encodeURIComponent(password) +
"&grant_type=password";
let headers = new Headers();
headers.append("Content-Type", "application/x-www-form-urlencoded");
let options = new RequestOptions({ headers: headers });
return this._http.post(SystemConstants.BASE_API + '/api/oauth/token', body, options).map((response: Response) => {
let user: LoggedInUser = response.json();
if (user && user.access_token) {
localStorage.removeItem(SystemConstants.CURRENT_USER);
localStorage.setItem(SystemConstants.CURRENT_USER, JSON.stringify(user));
}
});
}
logout() {
localStorage.removeItem(SystemConstants.CURRENT_USER);
}
isUserAuthenticated(): boolean {
let user = localStorage.getItem(SystemConstants.CURRENT_USER);
if (user != null) {
return true;
}
else
return false;
}
Here is my app.component.ts
export class AppComponent implements OnInit {
// the user object got from localStore
ngOnInit() {
this.user = JSON.parse(localStorage.getItem(SystemConstants.CURRENT_USER));
console.log(this.user);
}
}
The problem i got is i cant update the navbar to change in right state (It still work, i have the token but i have to refresh the whole page to update the nav bar)
How can i update the navigation bar in angular way? Thanks
As i understood your problem it is: How to hide "login" link located on main component after user signed himself in
I can think about solution like following:
Inside your AuthService you can add public boolean member "isLoggedIn":
#Injectable()
export class AuthService {
isLoggedIn = false;
}
You can share this service between components
Inside login component you can set isLoggedIn to true after successful login
login(){
this.auth.isLoggedIn = true
}
In your app.component you can subscribe to NavigationEnd event of the router :
export class AppComponent {
constructor(
private router: Router, private auth:AuthService){}
ngOnInit() {
this.router.events.subscribe(event => {
if (event.constructor.name === "NavigationEnd") {
this.isLoggedin = this.auth.isLoggedIn;
}
})
}
}
And then, in app component template you can show "login" menu with *ngIf="!isLoggedin"
here is plunker
hope it helps...