0

The user logs into the application where it checks for the loginID. If validated, the user details are returned through the authentication service. Now I am setting firstName, lastName, and id in sessionStorage to display the name on the sidebar immediately as user logs in. But firstName and lastName are only displayed either on home page refresh else it displays nothing or the previous user's name. On logout, I am removing the items from the sessionStorage. Login Routing code

this.authenticationService.login(this.username)
            .pipe(first())
            .subscribe(
                data => {
                    this.router.navigate([this.returnUrl]);
                },

Routing Code:

 { path: '', component: HomepageComponent, canActivate: [AuthGuard] },
  { path: 'login', component: LoginComponent, },
  { path: 'homepage', component: HomepageComponent, canActivate: [AuthGuard]},

AuthGuard Code:

 canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const currentUser = this.authenticationService.currentUserValue;
        if (currentUser) {
            return true;
        }
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
        return false;
    }

Code:

function authenticate() {
            const { username } = body;
            const user = users.find(x => x.username === username);
           
            if (!user) return error('wrong username');
            return ok({
                id: user.id,
                username: user.username,
                firstName: user.firstName,
                lastName: user.lastName,
                token: 'fake-jwt-token'
            })  

Authentication Service on login and logout. At login, the username is passed to the login method and then sent for validation to authenticate method. The response is then returned.

login(username: string) {
        return this.http.post<any>(`${environment.api}/users/authenticate`, { username })
            .pipe(map(user => {
                sessionStorage.setItem('firstName', JSON.stringify(user.firstName));
                sessionStorage.setItem('lastName', JSON.stringify(user.lastName));
                sessionStorage.setItem('id', JSON.stringify(user.id));
                this.currentUserSubject.next(user);
                return user;
            }));
    }

    logout() {
        sessionStorage.removeItem('firstName');
        sessionStorage.removeItem('lastName');
        sessionStorage.removeItem('id');
        this.currentUserSubject.next(null);
    }

In sidebar typescript:

ngOnInit() {
    this.firstName = JSON.parse(sessionStorage.getItem('firstName'));
    this.lastName = JSON.parse(sessionStorage.getItem('lastName'));
  }

In sidebar HTML:

<p>Welcome {{firstName}} {{lastName}}</p>

I want the sessionStorage to remove the previous details as soon as the user logs out. And next user's name to be displayed as soon as the user logs in. It should not display name on page refresh after entering the page.

  • Looks like navigation from login to home page is not waiting for authentication service. Can you show the router code from login to home page. – Arun205 Jul 17 '20 at 12:54
  • there could be multiple root causes, please create a simple stackblitz for it – Reza Jul 17 '20 at 13:00
  • I have added the router code as well. And it is a complex functionality. I tried creating a stackblitz but it didnt work out –  Jul 17 '20 at 13:11

1 Answers1

0

It has to do with the ngOninit for the sidebar component. If this is initialised before the storage is set, it will never get a value. Same goes for logging out. The this.firstName value is never updated. What you could do is create a behaviorsubject which returns the user fields and is filled on initialisation or when the user is logging in.

This is the most logical fix for your problem given the code you've provided.

EDIT: Checking more of your code, you already have the subject to subscribe on (currentUserSubject). So if you subscribe to this in your sidebar component it should work.

EDIT 2:

In the authenticationService you have the currentUserSubject subject (i’d change this to a behavoirSubject, will explain later). This has the logged in user for the application. By subscribing to this behaviorSubject you’ll always have the newest value available in the places you subscribe. So also the sidebar component. Upon logging out, you just emit a new empty value to currentUserSubject. This will then clear the sidebar component values.

Upon initialisation of your application you could read your local storage and emit that to your behaviourSubject. This will ensure that if the localstorage is set, the sidebar has it’s values.

The code you will use upon loggin in is behaviorSubject.next(uservalue) and for logging out you could use behaviorSubject.next(null). Then wherever you subscribe you just check wether uservalue is present and handle it accordingly.

The reason to use a behaviorSubject instead of a subject has to do with holding values. The behaviourSubject has to be created with a initial value and will always emit the latest value, while a subject only emits all the values after you’ve subscribed to it.

EDIT 3: Here is the code example

export class AuthService {
  private authenticatedUser = new BehaviorSubject<User>(undefined);

  constructor(private http: HttpClient) {
    this.initSubject();
  }

  public getAuthenticatedUser = this.authenticatedUser.asObservable();

  private initSubject(): void {
    if (hasLocalStorage) {
      // Fill yourself :) Here you check if there is data in the localStorage
      this.authenticatedUser.next(localStorage); // Here you pass in the localstorage data in the same format the login method does.
    } else {
      this.authenticatedUser.next(undefined);
    }
  }

  login(username: string) {
    return this.http
      .post<any>(`${environment.api}/users/authenticate`, { username })
      .pipe(
        map((user) => {
          sessionStorage.setItem('firstName', JSON.stringify(user.firstName));
          sessionStorage.setItem('lastName', JSON.stringify(user.lastName));
          sessionStorage.setItem('id', JSON.stringify(user.id));
          this.authenticatedUser.next(user);
          return user;
        })
      );
  }

  public logOut(): void {
    this.authenticatedUser.next(undefined);
    // And clear local storage
  }
}

export class SidebarComponent implements OnInit {

  public firstName: string = '';
  public lastName: string = ''

  constructor(private authService: AuthService) {}

  ngOnInit() {
    this.authService.getAuthenticatedUser.subscribe((user) => {
      if (user) {
        // set firstname and lastname
        this.firstName = user.firstName;
        this.lastName = user.lastName;
      } else {
        // not logged in
      }
    });
  }
}

Bas
  • 337
  • 2
  • 16
  • I guess you pointed out the issue correctly. Can you please explain bit in detail, how would ```behaviorSubject``` work? The sidebar component is actually used instead of in the app.html file. As it is always fixed to the left side of the app no matter what component is rendered. I guess thats why ```ngOnIt``` is not working. Can you tell me where my changes would be to fix this. Thank you –  Jul 17 '20 at 13:20
  • Sure, I'll create an edit in a few minutes :) Just have to type out a clear explanation – Bas Jul 17 '20 at 13:22
  • okay sure. Thank you. i really appreciate it. I am new to it so facing some issues –  Jul 17 '20 at 13:23
  • the ```currentUserSubject``` I tried accessing that once, but it didnt work. The values stored in ```sessionStorage``` how can I push those into ```currentUserSubject``` and later clear it. Thank you. –  Jul 17 '20 at 13:25
  • Thank you for the edits @Bas. I have changed it to ```behaviorSubject``` and I understand now that how the value of current user is stored or removed using ```behaviorSubject``` but can you tell me how can I pull that value in sidebar.ts file. I mean what will be the syntax to subscribe to the ```behaviorSubject``` in ts file. –  Jul 17 '20 at 13:40
  • I already have this code: ```constructor(private http: HttpClient) { this.behaviorSubject = new BehaviorSubject(JSON.parse(sessionStorage.getItem('currentUser'))); this.currentUser = this.behaviorSubject.asObservable(); } public get currentUserValue(): User { return (JSON.parse(sessionStorage.getItem('currentUser'))); }``` –  Jul 17 '20 at 13:43
  • Will do in a few minutes! [This (not sponsored)](https://www.udemy.com/course/the-complete-guide-to-angular-2/) is a good place to learn all of angular and it has a part about RxJS, where they talk about this and subjects in general – Bas Jul 17 '20 at 13:43
  • Thank you. I will look forward to your update and in the meantime I will go through the link. Thanks a a ton –  Jul 17 '20 at 13:44
  • This should help you make a start. If not, let me know :) – Bas Jul 17 '20 at 13:57
  • Thanks a ton @Bas. Just two quick questions I had: ```if (hasLocalStorage) { // Fill yourself :) this.authenticatedUser.next(localStorage);``` what logic should I fill here? Are ``` hasLocalStorage and localStorage``` to be defined on top and what will be there type. Also, how will I access this thing in ```sidebar.component.ts``` file? –  Jul 17 '20 at 14:10
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/218037/discussion-between-bas-and-vishavgeet). – Bas Jul 17 '20 at 14:13
  • Hi Bas, This answer worked perfectly. I have another piece related to this one. I have a service ABC. I have to add the id taken from user and stored in ```sessionStorage``` into the api endpoint. Now I tried subscribing as we did in ```sidebar```component but I guess I cannot inject a service inside a service. Code I have is: ```export class ABCService{ id = JSON.parse(sessionStorage.getItem('id')); constructor(private http: HttpClient, ) {} getPlan(): Observable { return this.http.get(this.apiUrl + `/all/` + this.id) .pipe(map((response) => { return response })) } }``` –  Jul 18 '20 at 09:07
  • I’ve responded in the chat – Bas Jul 18 '20 at 09:21
  • Hi Bas, I pinged you once in the chat. I tried it how you had mentioned and facing some issues. Can you please suggest how to proceed from there. Thank you –  Jul 19 '20 at 05:59