I have a simple Angular.io app. (angular-cli / 4.1.0)
I have a NavbarComponent that renders the username.
When accessing the app the first time I am not logged in and my app redirects to the LoginComponent. My NavBar is also rendered but without a username. After successful login I am redirected to my HomeComponent.
And here is the problem. My NavBar does not show the username. But if I do a refresh/ctrl+r the username is rendered.
What is wrong?
app.component.html
<nav-bar></nav-bar>
<router-outlet></router-outlet>
navbar.compoment.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'nav-bar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
me;
ngOnInit() {
this.me = JSON.parse(localStorage.getItem('currentUser'));
}
}
login.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { AlertService, AuthenticationService } from '../_services/index';
@Component({
moduleId: module.id,
templateUrl: 'login.component.html'
})
export class LoginComponent implements OnInit {
model: any = {};
loading = false;
returnUrl: string;
constructor(
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService,
private alertService: AlertService) { }
ngOnInit() {
// reset login status
this.authenticationService.logout();
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
}
login() {
this.loading = true;
this.authenticationService.login(this.model.email, this.model.password)
.subscribe(
data => {
this.router.navigate([this.returnUrl]);
},
error => {
this.alertService.error(error);
this.loading = false;
this.errorMsg = 'Bad username or password';console.error('An error occurred', error);
});
}
}
As mentioned by JusMalcolm, OnInit doesn't run again.
But you could use a Subject to tell the NavbarComponent to fetch the data from local storage.
In your NavBarComponent import Subject and declare it:
import { Subject } from 'rxjs/Subject';
....
public static updateUserStatus: Subject<boolean> = new Subject();
Then subscribe in your constructor:
constructor(...) {
NavbarComponent.updateUserStatus.subscribe(res => {
this.me = JSON.parse(localStorage.getItem('currentUser'));
})
}
And in your LoginComponent, import your NavbarComponent and when you have successfully logged in, just call next() on the Subject, and NavbarComponent will subscribe to it.
.subscribe(
data => {
NavbarComponent.updateUserStatus.next(true); // here!
this.router.navigate([this.returnUrl]);
},
// more code here
Also you can use a shared Service to tell NavbarComponent to re-execute the retrieval of the user. More about shared service from the Official Docs.
If you can return the name from backend while authenticating, you could inject the AuthenticationService to the NavbarComponent and bind the required name in navbar.component.html.
NavbarComponent:
export class NavbarComponent {
...
constructor(private authservice: AuthenticationService) {}
...
}
navbar.component.html:
<span>Welcome {{authservice.name}}!</span>
Since the component has already been initialized, ngOnInit() does not run after logging in. One solution for your case is to subscribe to a router param that checks whether the user is logged in.
eg.
this.route.queryParams
.map(params => params['loggedIn'])
.subscribe(loggedIn => {
if (loggedIn) {
this.me = JSON.parse(localStorage.getItem('currentUser'));
}
});