The title is pretty self explanatory. get role(): string
doesn't return the string initializer. For ex. it returns "Administrator", instead of a "Head Administrator". Role["Administrator"]
would do the thing, but IUser.role
is actually Role | undefined
, which is the problem and I can't simply do
get role(): string {
return Role[this.authService.userInfo?.role];
}
because of that.
import { Component } from '@angular/core';
import { AuthService } from '@core/services';
import { LayoutComponent } from '../layout.component';
@Component({
selector: 'app-header',
templateUrl: './header.component.html'
})
export class HeaderComponent {
get username(): string {
return this.authService.userInfo?.username || '';
}
get role(): string {
return this.authService.userInfo?.role || '';
}
constructor(private authService: AuthService, public app: LayoutComponent) {}
signOut(event: any) {
this.authService.signOut();
event.preventDefault();
}
}
export interface IUser extends IEntity {
email: string;
username: string;
role: Role;
}
export enum Role {
Administrator = 'Head Administrator',
DepartmentAdministrator = 'Department Administrator',
User = 'Super User'
}
import { Injectable, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
import { delay, map, tap } from 'rxjs/operators';
import { environment } from '@env';
import { IAuthResponse, IUser } from '@core/models';
import { INITIAL_AUTH_STATE } from '@core/constants';
import { JwtService } from '../util';
@Injectable({
providedIn: 'root'
})
export class AuthService implements OnDestroy {
private readonly TOKEN_URL = `${environment.apiUrl}/Accounts/token`;
private currentUserSubject = new BehaviorSubject<IAuthResponse>(INITIAL_AUTH_STATE);
private timer!: Subscription;
currentUser$: Observable<IAuthResponse> = this.currentUserSubject.asObservable();
get userInfo(): IUser | null {
const accessToken = this.currentUserValue?.accessToken;
return accessToken ? this.jwtService.decodeToken<IUser>(accessToken) : null;
}
private get currentUserValue(): IAuthResponse | null {
return this.currentUserSubject.value;
}
private get localStorageCurrentUser(): IAuthResponse {
const localStorageUser = localStorage.getItem('currentUser');
return localStorageUser ? JSON.parse(localStorageUser) : INITIAL_AUTH_STATE;
}
constructor(
private httpClient: HttpClient,
private router: Router,
private jwtService: JwtService
) {
this.currentUserSubject.next(this.localStorageCurrentUser);
window.addEventListener('storage', this.storageEventListener.bind(this));
}
ngOnDestroy(): void {
window.removeEventListener('storage', this.storageEventListener.bind(this));
}
signIn(username: string, password: string): Observable<IAuthResponse> {
const TOKEN_URL: string = this.TOKEN_URL + '/create';
return this.httpClient
.post<IAuthResponse>(TOKEN_URL, {
username,
password
})
.pipe(
map((res) => {
if (res && res.accessToken) {
this.setCurrentUser(res);
}
return res;
})
);
}
signOut(): void {
this.clearCurrentUser();
this.router.navigate(['auth']);
}
refreshToken(): Observable<IAuthResponse | null> {
const refreshToken = this.currentUserValue?.refreshToken;
if (!refreshToken) {
this.clearCurrentUser();
return of(null);
}
return this.httpClient.post<IAuthResponse>(`${this.TOKEN_URL}/refresh`, { refreshToken }).pipe(
map((res) => {
this.setCurrentUser(res);
return res;
})
);
}
private setCurrentUser(user: IAuthResponse): void {
this.currentUserSubject.next(user);
this.setLocalStorage(user);
this.startTokenTimer();
}
private clearCurrentUser(): void {
this.currentUserSubject.next(INITIAL_AUTH_STATE);
this.clearLocalStorage();
this.stopTokenTimer();
}
private setLocalStorage(userState: IAuthResponse): void {
localStorage.setItem('currentUser', JSON.stringify(userState));
localStorage.setItem('login-event', 'login' + Math.random());
}
private clearLocalStorage(): void {
localStorage.removeItem('currentUser');
localStorage.setItem('logout-event', 'logout' + Math.random());
}
private getTokenRemainingTime(): number {
const expiresAtUtc = this.currentUserValue?.expiresAtUtc;
if (!expiresAtUtc) {
return 0;
}
const expires = new Date(expiresAtUtc);
return expires.getTime() - Date.now();
}
private startTokenTimer(): void {
const timeout = this.getTokenRemainingTime();
this.timer = of(true)
.pipe(
delay(timeout),
tap(() => this.refreshToken().subscribe())
)
.subscribe();
}
private stopTokenTimer(): void {
this.timer?.unsubscribe();
}
private storageEventListener(event: StorageEvent): void {
if (event.storageArea === localStorage) {
if (event.key === 'logout-event') {
this.currentUserSubject.next(INITIAL_AUTH_STATE);
}
if (event.key === 'login-event') {
location.reload();
}
}
}
}
The code you have written is correct.
I have experimented in an isolated playground and created 2 functions: first to return the string initializer and second to return enum value name.
enum Role {
Administrator = 'Head Administrator',
DepartmentAdministrator = 'Department Administrator',
User = 'Super User'
}
function getRoleName1(role: Role): string {
return role
}
function getRoleName2(role: Role) {
const roleEnum = Role as Record<string, string>
for (const key of Object.keys(roleEnum)) {
if (roleEnum[key] === role) {
return key
}
}
return undefined
}
console.log('1:', getRoleName1(Role.Administrator)) // Head Administrator
console.log('2:', getRoleName2(Role.Administrator)) // Administrator
The first function is doing what you need and I see that in your code you have it written in a similar way.
I suspect that it doesn't work as expected because the enum doesn't match what your back-end actually returns, i.e. it might be returning 'Administrator' instead of 'Head Administrator'.