I have an issue with my template that you can see below. I think that I understand the problem, but solution is eluding me. When I put valid data to my local storage to fulfill my condition in constructor and reload the page. My subject has null value at first, but later when subscription is completed subject has a value. It leads to an issue in my template. First it show my Login button for split second, then dropdown is shown. I would like to achive that dropdown is shown right away (if jwtToken is in localStorage).
@Injectable({
providedIn: 'root'
})
export class AuthService {
authUser$: Subject<User | null> = new Subject<User | null>();
constructor(private http: HttpClient) {
const jwtToken: string | null = localStorage.getItem(TOKEN_KEY);
if(jwtToken) {
const id: number = this.getIdFromToken(jwtToken);
this.getUser(id).subscribe();
}
}
getUser(id: number): Observable<User> {
return this.http.get<UserResponse>(BASE_URL + `/api/user/${id}`)
.pipe(
map((response: UserResponse) => {
var user: User = {
username: response.username,
enabled: response.enabled,
locked: response.locked,
created: response.created,
authorities : response.authorities.map((a: { authority: string }) => a.authority)
};
this.authUser$.next(user);
return user;
}),
);
}
}
Here goes my template
<button *ngIf="(authService.authUser$ | async) == null" type="button" class="btn btn-primary me-2" (click)="authenticate()">Login</button>
<div *ngIf="authService.authUser$ | async" class="dropdown text-end pe-3">
<a href="#" class="dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<img src="favicon.ico" alt="mdo" height="40" width="40" class="rounded-circle">
</a>
<ul class="dropdown-menu" style="">
<li><a class="dropdown-item" href="#">Settings</a></li>
<li><a class="dropdown-item" href="#">Profile</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" (click)="logout()">Sign out</a></li>
</ul>
</div>
I don't really know what to do with the issue, chatgpt didn't helped and I didn't fond any hint on internet
Thank you all. Based on your tips I managed to edit the code.
export interface Auth {
user: User | null,
token: string | null,
error: Error | null,
status: "initialize" | "error" | "login" | "logout",
}
@Injectable({
providedIn: 'root'
})
export class AuthService {
authSubject =
new BehaviorSubject<Auth>({
status: "initialize",
token: localStorage.getItem(TOKEN_KEY),
error: null,
user: null,
});
constructor(private http: HttpClient) {
if(this.authSubject.value.token) {
this.getUserByToken(this.authSubject.value.token).subscribe();
} else {
this.authSubject.next({ token: null, status: "logout", error: null, user: null, });
}
}
getUserByToken(token: string): Observable<User> {
let id: number = this.getIdFromToken(token);
return this.getUserById(id);
}
getUserById(id: number): Observable<User> {
return this.http.get<UserResponse>(BASE_URL + `/api/user/${id}`)
.pipe(
map((response: UserResponse) => {
const user = {
username: response.username,
enabled: response.enabled,
locked: response.locked,
created: response.created,
authorities: response.authorities.map((a: { authority: string }) => a.authority),
} as User
this.authSubject.next({ token: this.authSubject.value.token, status: "login", error: null, user, });
return user;
}),
catchError(error => {
this.authSubject.next({ token: null, status: "error", error, user: null, });
return throwError(() => error);
})
);
}
}
Template:
<div *ngIf="auth?.status !== 'initialize'" class="d-flex">
<ng-template [ngIf]="auth?.status === 'login'" [ngIfElse]="elseBlock">
<div class="dropdown text-end pe-3">
<a href="#" class="dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<img src="favicon.ico" alt="mdo" height="40" width="40" class="rounded-circle">
</a>
<ul class="dropdown-menu" style="">
<li><a class="dropdown-item" href="#">Settings</a></li>
<li><a class="dropdown-item" href="#">Profile</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" (click)="logout()">Sign out</a></li>
</ul>
</div>
</ng-template>
<ng-template #elseBlock>
<button type="button" class="btn btn-primary me-2" (click)="authenticate()">Login</button>
</ng-template>
<app-theme-switcher/>
</div>