Issue
I created a simple Authentication service that works well at the start of the web app , however in a user-case scenario where the user decides to log-out and relog-in the toolbar/navbar remain visible when switching back to the login page, in order for it to become invisible the page needs to be refreshed so the ngIf
method would work; my idea was to show the toolbar/navbar whenever the user is logged in, which means if there is a token cached in localStorage.
this is the code that applies the toolbar/navbar
app.component.html
<div class="main" >
<mat-toolbar *ngIf="userToken">
<div class="MenuBar">
<button mat-icon-button style="margin:auto" color="primary" (click)="opened=!opened" aria-label="Example icon button with a menu icon">
<mat-icon>menu</mat-icon>
</button>
<button mat-raised-button color="primary" routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Home</button>
</div>
<span class="spacer"></span>
<button mat-stroked-button id="Users" style="margin-right: 16px" routerLink="/users" routerLinkActive="active">Profile</button>
<button mat-raised-button color="accent" (click)="logout()">Logout</button>
</mat-toolbar>
<mat-sidenav-container class="container" >
<mat-sidenav mode="push" [(opened)]=opened >
<p>Working</p>
</mat-sidenav>
<mat-sidenav-content>
<div class="app-container">
<alert></alert>
<router-outlet></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
app.component.ts
import { Component,ChangeDetectorRef } from '@angular/core';
import { AccountService } from '../app/services/account.service';
import { User } from '../app/models/user';
import { Token } from '@angular/compiler/src/ml_parser/lexer';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'FrontEnd';
userToken: Token;
opened=false;
constructor(private accountService : AccountService) {
this.userToken = this.accountService.userToken;
}
logout() {
this.accountService.logout();
}
}
app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { LoginComponent } from './authentification/login/login.component'
import { MainComponent } from './main/main.component';
import { UsersModule } from './Users CRUD/users.module';
import { AuthGuard } from './_helpers/auth.guard';
const authModule=()=> import('./authentification/authentification.module').then(x => x.AuthentificationModule);
const usersModule = () => import('./Users CRUD/users.module').then(x => x.UsersModule);
const routes: Routes = [
{ path : '', component: MainComponent , canActivate: [AuthGuard]},
{ path: 'users', loadChildren: usersModule, canActivate: [AuthGuard] },
{ path : 'auth',loadChildren: authModule},
{ path : '**', redirectTo:''}
];
@NgModule({
imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
exports: [RouterModule]
})
export class AppRoutingModule { }
account.service.ts
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { User } from '../models/user';
@Injectable({ providedIn: 'root' })
export class AccountService {
private userSubject: BehaviorSubject<User>;
public user: Observable<User>;
readonly BaseURL ='https://localhost:44381/api';
public subj = new BehaviorSubject({});
constructor(
private router: Router,
private http: HttpClient
) {
this.userSubject = new BehaviorSubject<User>(null);
this.user = this.userSubject.asObservable();
}
public get userToken() {
return JSON.parse(JSON.stringify(localStorage.getItem('user')));
}
login(username, password) {
return this.http.post<User>(`${this.BaseURL}/User/Login`, { username, password });
/* .pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(user));
this.userSubject.next(user);
return user;
}));*/
}
logout() {
// remove user from local storage and set current user to null
localStorage.removeItem('user');
this.userSubject.next(null);
this.router.navigate(['/auth/login']);
}
GetUser()
{
return this.http.get(this.BaseURL+'/UserProfile');
}
register(user: User) {
return this.http.post(`${this.BaseURL}/User/Register`, user);
}
getAll() {
return this.http.get<User[]>(`${this.BaseURL}/users`);
}
getById(id: string) {
return this.http.get<User>(`${this.BaseURL}/UserProfile/${id}`);
}
}
Solution
Thanks to @kin I was able to understand the code better and come up with a solution to my problem.
I Fixed this by setting the user token in localstorage inside the login
method in the service.ts
file then subscribing to the user in the component.ts
file
here’s the code :
account.service.ts
...
login(username, password) {
return this.http.post<User>(`${this.BaseURL}/User/Login`, { username, password }).pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', user.token);
this.userSubject.next(user);
return user;
}));
}
...
app.component.ts
...
constructor(private accountService : AccountService) {
this.accountService.user.subscribe(x => this.user = x);
}
...
app.component.html
<div class="main" >
<mat-toolbar *ngIf="user" >
<div class="MenuBar">
<button mat-icon-button style="margin:auto" color="primary" (click)="opened=!opened" >
<mat-icon>menu</mat-icon>
</button>
<button mat-raised-button color="primary" routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" (click)="opened=false">Home</button>
</div>
<span class="spacer"></span>
<button mat-stroked-button id="Users" style="margin-right: 16px" routerLink="/users" routerLinkActive="active" (click)="opened=false">Profile</button>
<button mat-raised-button color="accent" (click)="logout()">Logout</button>
</mat-toolbar>
<mat-sidenav-container >
<mat-sidenav mode="push" [(opened)]=opened >
<p>Working</p>
</mat-sidenav>
<mat-sidenav-content>
<div class="app-container">
<alert></alert>
<router-outlet></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
I hope this helps anyone with a similar problem