I am building a basic application in Angular demonstrating authentication and I'm getting my ass kicked by Promises.
I've read at least 20 articles, and I think I get how promises work -- they return when they feel like it and you have to keep that in mind -- but I can't seem to "escape" from them. I need a boolean
value in the end, and I can't seem to get that.
Here's what I'm trying:
import { Injectable, OnInit } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class AuthService {
isAuthenticated: boolean = false;
public isLoggedIn(): boolean {
console.log('isLoggedIn before: ' this.isAuthenticated);
this.checkUser();
console.log('isLoggedIn after: ' this.isAuthenticated);
return this.isAuthenticated;
}
public async checkUser(): Promise<void> {
// myAuthenticationObject only has userInfo if the user is logged in
await myAuthentication0bject.userInfo().then((user) => {
this.isAuthenticated = user !== undefined;
});
}
}
This doesn't work because the checkUser
call returns before isAuthenticated
is set despite the fact that checkUser
has async
defined. (userInfo
is a Promise)
If I make isLoggedIn
an async
function so I can await checkUser
, then I'm right back where I started -- with a Promise that I can't get the boolean
out of.
I'm clearly missing something here (or maybe it's just not doable -- happy to accept that fact!) and could use some assistance.
CodePudding user response:
You have to wait the promise:
public async isLoggedIn(): boolean {
console.log('isLoggedIn before: ' this.isAuthenticated);
await this.checkUser();
console.log('isLoggedIn after: ' this.isAuthenticated);
return this.isAuthenticated;
}
public async checkUser(): Promise<void> {
// myAuthenticationObject only has userInfo if the user is logged in
let user = await myAuthentication0bject.userInfo();
this.isAuthenticated = user !== undefined;
}
CodePudding user response:
Nick here to answer you own question.
Alas -- Promise Resolution is one of the hardest things to learn in front end programming, especially if you come from native Windows programming.
So, it goes like this: Stop trying to be imperative. Just return a Promise, and then let the UI bind to that Promise, trusting that it will be resolved in due course.
So, here's what you need to do:
First, just embrace Promises and return one from your isLoggedIn()
call:
import { Injectable } from '@angular/core';
import { PassageUser } from '@passageidentity/passage-elements/passage-user';
@Injectable({
providedIn: 'root',
})
export class AuthService {
isAuthenticated: boolean = false;
myUser: PassageUser = new PassageUser();
constructor() {
}
public async isLoggedIn(): Promise<boolean> {
return await myAuthenticationObject.then(userInfo => {
this.isAuthenticated = userInfo !== undefined;
return this.isAuthenticated;
})
}
}
isAuthenticated
will be set in due course, and your UI will know that and properly deal with it. Then, in your Component, do this:
import { Component, OnInit } from '@angular/core';
import { environment } from 'src/environments/environment';
import { AuthService } from '../service/auth.service';
@Component({
selector: 'app-restricted',
templateUrl: './restricted.component.html',
styleUrls: ['./restricted.component.css'],
})
export class RestrictedComponent implements OnInit {
public isAuthenticated: boolean = false;
public token: string | null = '';
constructor(protected authService: AuthService) {}
ngOnInit(): void {
this.authService.isLoggedIn().then((result) => {
this.isAuthenticated = result;
this.token = localStorage.getItem('psg_auth_token');} );
}
}
Then the property fields will be set in due course, and your UI will properly handle all of that.
Next, in your HTML:
<div *ngIf="isAuthenticated">
<div >
Hey there!
</div>
<div >
You have successfully signed in!
<br /><br />
<p><b>Token</b> {{ token }}</p>
</div>
</div>
<div *ngIf="!isAuthenticated">
<div >
Unauthorized
</div>
<div >
Sorry -- you aren't allowed to see this stuff unless you are logged in.
<br /><br />
<a href="/login" className={styles.link}>Login to continue.</a>
</div>
</div>
Note that you use the isAuthenticated
variable, and not a direct call to the AuthService.
The whole idea here is that you sort of have to trust Angular to deal with the Promises, and not think about trying to be imperative. If you are fighting with Promises, stop. Instead, embrace them, and let the framework deal with them.
Your imperative programming days are over. Embrace the asynchronous world!