Reputation: 114
In my navbar has a dropdown for login and for the user options. Using ngIf I show and hide each menu as appropriate. For example, when the user isn't logged I show this:
But, when some user is logged I show this one:
I did this using asking to my backend if the token is valid and the user is logged. And I do it using a subscription. The problem is that when I click "Sign in" or "Sign out" the component does not reload. I need to reload the page for it to work. What is the best method to get a new value from a subscription without reload all the page? Because I don't think reload all the page were a good experience for user.
This is my code:
navbar.component.html
<ul class="navbar-nav mr-right py-0">
<li *ngIf="!isLogged; else elseBlock" class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Sign in or register
</a>
<div class="dropdown-menu p-3 dropdown-menu-right">
<app-login></app-login>
</div>
</li>
<ng-template #elseBlock>
<li class="nav-item dropdown">
<a class="nav-link py-0" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img src="{{avatarUrl}}" class="rounded-circle d-inline-block p-0 profile-img mx-3" alt="{{username}} avatar">
<span class="dropdown-toggle p-0 m-0 align-middle">{{username}}</span>
</a>
<ul class="dropdown-menu-right dropdown-menu" aria-labelledby="dropdownMenuLink">
<li><span class="dropdown-item clickable" (click)="goToProfile()">Profile</span></li>
<li><span class="dropdown-item clickable" (click)="logout()">Sign out</span></li>
</ul>
</li>
</ng-template>
</ul>
navbar.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { AccountService } from '../account/account.service';
import { AuthService } from '../auth/auth.service';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit {
isLogged: boolean;
avatarUrl: string;
username: string;
isLogged_subscription: Subscription;
avatar_subscription: Subscription;
username_subscription: Subscription;
constructor(private authService: AuthService, private accountService: AccountService, private router: Router) { }
ngOnInit(): void {
this.isLogged_subscription = this.authService.isLogged().subscribe(
value => {
this.isLogged = value;
}
);
this.avatar_subscription = this.accountService.getAvatar().subscribe(
value => {
this.avatarUrl = value;
}
);
this.username_subscription = this.accountService.getUsername().subscribe(
value => {
this.username = value;
}
);
}
logout(): void {
this.authService.logout();
}
goToProfile(): void {
this.router.navigateByUrl('/profile')
}
}
auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { tap } from 'rxjs/operators';
import { Observable, BehaviorSubject } from 'rxjs';
import { UserI } from '../models/user';
import { ServerResponseI } from '../models/server-response';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private httpClient: HttpClient) { }
register(user: UserI): Observable<ServerResponseI> {
return this.httpClient.post<ServerResponseI>(`${environment.apiBase}/account/register`, user).pipe(
tap(
(res: ServerResponseI) => {
if(res){
this.saveToken(res.token, res.expiry)
}
}
)
);
}
login(user: UserI): Observable<ServerResponseI> {
return this.httpClient.post<ServerResponseI>(`${environment.apiBase}/account/get_token/`, user).pipe(
tap(
(res: ServerResponseI) => {
if(res){
this.saveToken(res.token, res.expiry)
}
}
)
);
}
isLogged(): Observable<boolean> {
return this.httpClient.get<boolean>(`${environment.apiBase}/account/is_logged/`);
}
logout(): void{
localStorage.removeItem("ACCESS_TOKEN");
localStorage.removeItem("EXPIERES_IN");
}
private saveToken(token: string, expiresIn: string): void {
localStorage.setItem("ACCESS_TOKEN", token);
localStorage.setItem("EXPIERES_IN", expiresIn);
}
}
Upvotes: 0
Views: 189
Reputation: 91
The reason why you need to reload the page to see changes is because you only update "isLogged" variable in ngOnInit.
I'll start with logout since I don't see the code for the login component.
The simplest solution would look like this:
logout(): void {
this.authService.logout();
this.isLogged = false;
}
If you want to make sure that user is logged out by asking you back-end, then you can do it like this:
logout(): void {
this.authService.logout();
this.updateLoginStatus();
}
// extracting this logic into a separate function gives us an ability
// to use it here and in ngOnInit
updateLoginStatus(): void {
// no need to save subscription if we don't plan to unsubscribe later
this.authService.isLogged().subscribe(value => this.isLogged = value)
}
To update your component after login you could create an event emitter in your login component and bind to it in navbar, which would look like this:
export class LoginComponent {
@Output() loggedIn = new EventEmitter()
// function that handles click on "Log In" button
buttonHandler() {
// ... some of your login
this.loggedIn.emit()
}
}
After that you need to listen for this event in your navbar.component.html like this:
<app-login (loggedIn)="updateLoginStatus()"></app-login>
Or simpler version:
<app-login (loggedIn)="isLogged = true"></app-login>
Upvotes: 1