Reputation: 373
I have an Angular app with navigation bar. This navbar has 2 states - for logged in users and for users that are not logged yet. My goal is to change this navbar state via *ngIf
statement and ng-template
block so logged in user can see his name and avatar. This part works fine, except that user's data doesn't load if I use ngAfterContentInit
hook, but it works as it should if I use ngDoCheck
or ngAfterViewChecked
hooks. These solutions don't suit me because they cause too much requests to the backend, and I need only one request after user logs in to set his data. Here is HTML part just for reference:
<nav *ngIf="subscr | async as auth; else elseBlock" class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top">
<div class="img-logo-block">
<img class="navbar-brand" src="assets/static/img/logo.svg" alt="Logo" (click)="gt_myprofile()">
</div>
<div class="container">
<div class="collapse navbar-collapse navmenu" [ngClass]="{ 'show': navbarOpen }" id="navbarSupportedContent">
<ul class="navbar-nav">
<li class="nav-item sub">
<a class="nav-link unselectable" (click)="gt_myprofile()"><span
class="icon icon-profile"></span> profile
</a>
</li>
<li class="nav-item sub">
<a class="nav-link unselectable" routerLink="/contacts" routerLinkActive="nav-link-active"><span
class="icon icon-contacts"></span>
contacts
</a>
</li>
</ul>
<div ngbDropdown class="profile">
<a class="nav-dropdown dropdown-toggle" id="dropdownBasic1" ngbDropdownToggle>
{{user?.first_name}}
<img *ngIf="!user?.avatar;else userAvatar" src="assets/static/img/avatar.jpg" class="nav-avatar">
<ng-template #userAvatar><img src="{{user?.avatar}}" alt="" class="nav-avatar"></ng-template>
</a>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<a ngbDropdownItem class="profile-dropdown-link" (click)="gt_myprofile()">profile</a>
<a ngbDropdownItem class="profile-dropdown-link" (click)="gt_allContacts()">Contacts</a>
<a ngbDropdownItem class="profile-dropdown-link" (click)="gt_settings()">settings</a>
</div>
</div>
</nav>
<ng-template #elseBlock>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top">
<div class="collapse navbar-collapse" [ngClass]="{ 'show': navbarOpen }" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" routerLink="/about" routerLinkActive="nav-link-active">about<span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/cooperation" routerLinkActive="nav-link-active">cooperation</a>
</li>
</ul>
<!-- end navbar-nav -->
<div class="search-form">
<a class="nav-link" (click)="notif_on()" (click)="home()">Логин</a>
</div>
</div>
</div>
</nav>
</ng-template>
And TS component relevant part:
ngOnInit() {
this.subscr = this.auth_service.currentUser;
}
this hook works
ngAfterViewChecked() {
// check if user is logged in
if (localStorage.getItem('currentUser') !== null) {
this.auth_service.getUser().subscribe(
data => {this.user = data}
);
}
}
and this doesn't
ngAfterContentInit() {
// check if user is logged in
if (localStorage.getItem('currentUser') !== null) {
this.auth_service.getUser().subscribe(
data => {this.user = data}
);
}
}
Auth service relevant part:
private currentUserSubject: BehaviorSubject<User>;
public currentUser: Observable<User>;
constructor(private http: HttpClient) {
this.currentUserSubject = new BehaviorSubject<User>
(JSON.parse(localStorage.getItem('currentUser')));
this.currentUser = this.currentUserSubject.asObservable();
}
public get currentUserValue(): User {
return this.currentUserSubject.value;
}
login(email: string, password: string) {
return this.http.post<any>(`${environment.apiUrl}/api/auth/`, { email, password })
.pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
this.currentUser = this.currentUserSubject.pipe(filter(value => !!value)).asObservable();
return user;
}));
}
logout() {
// remove user from local storage to log user out
localStorage.removeItem('currentUser');
this.currentUserSubject.next(null);
}
Any help and tips would be appreciated.
Upvotes: 2
Views: 347
Reputation: 4453
After the meeting, finally I could understand what is the problem.
service code
// stored current user info
// tslint:disable-next-line: variable-name
private _user: User = JSON.parse(localStorage.getItem('currentUser'));
// current user subject
currentUserSubject: BehaviorSubject<User> = new BehaviorSubject(this.user);
// get current userInfo
get user() {
return this._user;
}
// set user info
set user(user: User) {
this._user = user;
if (this._user) {
localStorage.setItem("currentUser", JSON.stringify(this.user));
} else {
localStorage.removeItem("currentUser");
}
this.currentUserSubject.next(this.user);
}
get userChanges() {
return this.currentUserSubject.asObservable();
}
login(email: string, password: string) {
return this.http.post<any>("", { email, password }).pipe(
map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
this.user = user;
return this.user;
})
);
}
logout() {
this.user = null;
}
getUser(id: number = this.user.id) {
return this.http.get<any>((`${environment.apiUrl}/api/user/${id}`));
}
Navbar component code
ngOnInit() {
this.auth_service.userChanges.pipe(filter(user => !!user)).subscribe(user => {
this.auth_service.getUser(user.id).subscribe(
data => {this.user = data}
);
});
}
template code
<nav *ngIf="user; else elseBlock" class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top">
Upvotes: 1