Upon logging in, the Angular page fails to update with the latest information

Working on my initial login implementation in Angular. Everything seems to be functioning correctly - the user can log in, gets redirected to the GiveDashboardComponent, and the "login" button on the nav bar changes to "logout" accordingly. However, I noticed that these changes only take effect after manually refreshing the page. Is there a better way to handle this in Angular or did I make a mistake in my current code?

This is my LoginComponent:

export class LoginComponent implements OnInit {
  user: User = new User();
  errorMessage: string = '';

  constructor(
    private authService: AuthenticationService,
    private router: Router,
    public sharedService: SharedService
  ) {}

  ngOnInit(): void {
    if (this.authService.currentUserValue?.id) {
      this.router.navigate(['/give-dashboard']);
    }
  }

  exit() {
    this.sharedService.showLoginComponent = false;
  }

  login() {
    this.authService.login(this.user).subscribe(
      (data) => {
        this.router.navigate(['/give-dashboard']);
      },
      (err) => {
        this.errorMessage = 'Username or password is incorrect.';
        console.log(err);
      }
    );
  }
}

This is the Navigation bar component:

export class NavBarComponent implements OnInit {
  isLoggedIn = false;
  user: User = new User();

  constructor(
    private router: Router,
    private authService: AuthenticationService,
    private sharedService: SharedService
  ) {}

  ngOnInit(): void {
    this.authService.loggedUser.subscribe((res) => {
      if (res) {
        this.isLoggedIn = true;
      } else {
        this.isLoggedIn = false;
      }
    });
  }
  showLogin() {
    this.sharedService.showLoginComponent = true;
  }

  logout() {
    this.authService.logout();
    this.router.navigate(['']);
    this.isLoggedIn = false;
  }
}

And the Dashboard component (I attempted navigation but it's not working as expected)

export class GiveDashboardComponent implements OnInit {
  constructor(private router: Router) {}

  ngOnInit(): void {
    this.router.navigate(['/give-dashboard']);
  }
}

Finally, authenticationService:

export class AuthenticationService {
  public currentUser: Observable<User>;
  private currentUserSubject: BehaviorSubject<User>;
  private isLoggedIn = new BehaviorSubject<boolean>(this.userIsLoggedIn());
  public loggedUser = this.isLoggedIn.asObservable();

  constructor(private http: HttpClient) {
    let storageUser;
    const storageUserAsStr = localStorage.getItem('currentUser');
    if (storageUserAsStr) {
      storageUser = JSON.parse(storageUserAsStr);
    }
    this.currentUserSubject = new BehaviorSubject<User>(storageUser);
    this.currentUser = this.currentUserSubject.asObservable();
  }

  public get currentUserValue(): User {
    return this.currentUserSubject.value;
  }

  login(user: User): Observable<any> {
    return this.http.post<User>(API_URL + '/sign-in', user).pipe(
      map((res) => {
        if (res) {
          // set session-user
          this.setSessionUser(res);
        } else {
          return false;
        }
        return res;
      })
    );
  }

  register(user: User): Observable<any> {
    return this.http.post<User>(API_URL + '/sign-up', user).pipe(
      map((res) => {
        if (res) {
          this.setSessionUser(res);
        } else {
          return false;
        }
        return res;
      })
    );
  }

  setSessionUser(user: User) {
    localStorage.setItem('currentUser', JSON.stringify(user));
    this.currentUserSubject.next(user);
  }

  logout() {
    localStorage.removeItem('currentUser');
    this.currentUserSubject.next(new User());
    // this.currentUserSubject.next(null);
  }
  refreshToken(): Observable<any> {
    return this.http.post(
      API_URL + '/refresh-token?token=' + this.currentUserValue?.refreshToken,
      {}
    );
  }

  userIsLoggedIn() {
    return !!localStorage.getItem('currentUser');
  }
}

Answer №1

It seems like the issue stems from a change detection problem. One solution could be utilizing Observables and Subjects from the RxJS library. By allowing components to subscribe to changes and update accordingly, you can use a BehaviorSubject within your AuthenticationService to store the current user's state.

Here is an example of how you can implement the AuthenticationService:

 export class AuthenticationService {
  private currentUserSubject: BehaviorSubject<User | null>;
  public currentUser$: Observable<User | null>;

  constructor(private http: HttpClient) {
    let storageUser: User | null = null;
    const storageUserAsStr = localStorage.getItem('currentUser');
    if (storageUserAsStr) {
      storageUser = JSON.parse(storageUserAsStr);
    }
    this.currentUserSubject = new BehaviorSubject<User | null>(storageUser);
    this.currentUser$ = this.currentUserSubject.asObservable();
  }

  setSessionUser(user: User) {
    localStorage.setItem('currentUser', JSON.stringify(user));
    this.currentUserSubject.next(user);
  }

  logout() {
    localStorage.removeItem('currentUser');
    this.currentUserSubject.next(null);
  }
  // ... remaining code
}

In your NavBarComponent, make sure to subscribe to the currentUser:

TS.component of NavBarComponent:

export class NavBarComponent implements OnInit {
  isLoggedIn = false;

  constructor(
    private router: Router,
    private authService: AuthenticationService,
    private sharedService: SharedService
  ) {}

  ngOnInit(): void {
    this.authService.currentUser$.subscribe((user) => {
      this.isLoggedIn = !!user;
    });
  }

  // ... remaining code
}

With this approach, any changes in the user's status (logging in or out) will prompt AuthenticationService to update its currentUserSubject, which triggers updates for all subscribers.

Answer №2

Personally, my approach is to simplify things. If this were my code, I would just include the following line of code: location.reload(); here =>

login() {
    this.authService.login(this.user).subscribe(
      (data) => {

// Add this line and thank me later :)
        location.reload()
        this.router.navigate(['/give-dashboard']);
      },
      (err) => {
        this.errorMessage = 'Username or password is incorrect.';
        console.log(err);
      }

// Also, don't forget to add it here 

 ngOnInit(): void {
    if (this.authService.currentUserValue?.id) {
 
location.reload()
      this.router.navigate(['/give-dashboard']);
    }
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The specified argument, 'void', cannot be assigned to a parameter that expects 'SetStateAction | undefined'

Currently, I am engaged in a TypeScript project where I am fetching data from an endpoint. The issue arises when I attempt to assign the retrieved data to my state variable nft using the useState hook's setNft function. An error is being thrown specif ...

Updates to Angular form control values are not triggering notifications

I am currently working with a basic form control that subscribes to the valueChanges observable. @Component({ selector: 'my-app', template: ` <input [formControl]="control" /> <div>{{ name$ | async | json }}</div ...

Exploring the Nested JSON Data Loop with *ngFor in Angular 5/4

Recently I started working with Angular, and I've created a service to iterate over nested JSON data for my list. export const CATEGORIES: Category[] = [ { id: 1, categoryName:'Accessories', subcatName: [ {subcategory: & ...

Mastering Redux Toolkit - Ensuring Payload Type Verification is in Place when Invoking an Action Creator

I have been utilizing the redux toolkit for a while now and I appreciate how it helps in reducing redundant code. I am also interested in incorporating typescript, but I am facing challenges with getting the typechecking of an action payload to function pr ...

Am I on the right track with my use of Angular directives?

As I work on setting up a date picker with ngx-bootstrap's bsDatepicker, I noticed that the demo illustrates its setup as follows: <input bsDatepicker #dp="bsDatepicker"> <button (click)="dp.toggle()">Toggle</button> Although this ...

Troubleshooting problems with local references in an Angular date picker

I am currently working with an Angular date picker component and trying to access its values using a local reference. Unfortunately, when I attempt to console log the local reference, it returns undefined. The datepicker, function, and trigger are provid ...

When it comes to TypeScript, there is a limitation in assigning a value to an object key with type narrowing through the

I created a function called `hasOwnProperty` with type narrowing: function hasOwnProperty< Obj extends Record<string, any>, Prop extends PropertyKey, >( obj: Obj, prop: Prop, ): obj is Obj & Record<Prop, any> { return Object ...

Ways to dynamically manipulate HTML elements in Angular 5

Recently, I've been attempting to programmatically transform an HTML element. Strangely, when I update the transform value in the console tab, it changes successfully, but for some reason it doesn't reflect in the element tab of the browser. onD ...

What is the best way to include multiple modules in a single TypeScript declaration file?

I've encountered a peculiar issue with Module augmentation. I currently have an agument.d.ts file located in my src folder at <ROOT>/src/augment.d.ts. Within this file, I am defining a module for Webpack's raw-loader and extending the exist ...

Exploring Sequelize: Uncovering the Secret to Retrieving Multiple Associated Items of Identical Type

Within my database, I have a situation where there are two tables sharing relations of the same type. These tables are named UserCollection and ImagenProcess UserCollection has two instances that relate to ImagenProcess. Although the IDs appear unique whe ...

Exploring the intricacies of a specific Angular route through dialogue

Currently, I am working on enhancing the user experience by allowing data editing through dialogs. I have opted to move this functionality to a separate component so that it can read and edit the designated data efficiently without cluttering the main page ...

Describing an Object with some typed properties

Is there a method to specify only a portion of the object type, while allowing the rest to be of any type? The primary objective is to have support for intelliSense for the specified part, with the added bonus of type-checking support. To demonstrate, let& ...

Customizing the Android Back Button behavior in NativeScript for a single specific page

I am currently using NativeScript version 5.2.4 along with TypeScript. My goal is to disable the back button functionality in one specific page only, but I'm facing an issue where it also disables the back button behavior for child pages. Below is the ...

Capture and store the current ionic toggle status in real-time to send to the database

I have a list of names from the database that I need to display. Each name should have a toggle button associated with it, and when toggled, the value should be posted back to the database. How can I achieve this functionality in an Ionic application while ...

Creating aliases for routes in Angular

In my Angular 6 application, I am exploring the process of creating URL aliases. However, I have encountered a roadblock that I hope you can help me with: In my angular app, I currently have a URL defined as article/:id, which corresponds to the Articl ...

Ionic Native HTTP call retrieves Blob without proper data type

I am currently working on a code snippet that extracts image strings and encodes them as Blob objects. const reqOptions: any = { method: 'get', responseType: 'blob', headers: { accept: 'image/*' } } ion ...

Testing the Angular service by making a PATCH request

I am working on the following service: creerPass(mail: string, person: string, password: string): Observable<void> { const params = new HttpParams() .set('person', person) .set('mail', mail); return this.http. ...

What is the best way to make an https post request in angular?

When attempting to upload an image on Heroku, I encountered the following error message: Mixed Content: The page at '' was loaded over HTTPS, but requested an insecure image ''. This content should also be served over HTTPS. In additio ...

Error in Angular 2 (CLI) Routing: Unable to process (Error reading property 'split' of undefined)

What could be the reason why routing is only working for the first imported component after AppComponent (for example, "PageNonFound")? I also encountered an error after implementing routing in my project. Error Error in ./AppComponent class AppComponent ...

Asynchronously loading data with Angular Material's VirtualScrollViewPort

Exploring various examples of the VirtualScrollViewPort, I have encountered challenges in implementing them effectively. The samples provided load all data from the server at once, whereas I need to load data individually due to its large size. My specific ...