Navigating using the Angular router occurs before any other lines of code are executed

I am encountering an unusual issue with my Angular code involving a login page that interacts with an API.

login.component.ts:

export class LoginComponent implements OnInit {

  formData;

  constructor(private usersService: UsersService, private router: Router) { }

  ngOnInit() {
    this.formData = new FormGroup({
      login: new FormControl('', [Validators.required]),
      password: new FormControl('', [Validators.required])
    });
  }

  onClickSubmit(data) {
    this.usersService.login(data).subscribe((response) => {
      const token = response.token;
      const userLogin = response.user.userLogin;

      localStorage.setItem('token', token);
      localStorage.setItem('login', userLogin);

      this.router.navigate(['/home']);
    });
  }
}

login.component.html:

<div class="card mb-3">
    <div class="card-header">
      Login
    </div>
    <div class="card-body">
      <form [formGroup]="formData" (ngSubmit)="onClickSubmit(formData.value)" novalidate>
        <div class="form-group">
          <label for="inputLogin">Login</label>
          <input type="text" class="form-control" id="inputLogin" name="login" placeholder="Login" formControlName="login">
          <span *ngIf="(login.dirty || login.touched) && login.invalid && login.errors.required" class="error">Login is required</span>
        </div>

        <div class="form-group">
          <label for="inputPassword">Password</label>
          <input type="password" class="form-control" id="inputPassword" name="password" placeholder="Password" formControlName="password">
          <span *ngIf="(password.dirty || password.touched) && password.invalid && password.errors.required" class="error">Login is required</span>
        </div>

        <button type="submit" class="btn btn-primary" [disabled]="!formData.valid">Sign in</button>
      </form>
    </div>
    <p class="card-text login-text"><small class="text-muted">Don't have any account? <a routerLink="/register">Register</a></small></p>
  </div>

I observed that this.router.navigate() executes before localStorage.setIem.

users.service.ts:

export class UsersService {
  private apiUrl = 'https://localhost:63939/api/v1/';
  private registerUrl = this.apiUrl + 'identity/register';
  private loginUrl = this.apiUrl + 'identity/login';

  constructor(private httpClient: HttpClient) { }

  register(user: User): Observable<AuthResponse> {
    return this.httpClient.post<AuthResponse>(this.registerUrl, user);
  }

  login(user: LoginRequest): Observable<AuthResponse> {
    return this.httpClient.post<AuthResponse>(this.loginUrl, user);
  }
}

menu.component.ts:

export class MenuComponent implements OnInit {
  showLoginLink = true;
  userLogin;

  constructor() { }

  ngOnInit() {
    this.userLogin = localStorage.getItem('login');
    if (this.userLogin !== null) {
      this.showLoginLink = false;
    }
  }

}

menu.component.html

<li class="nav-item" *ngIf="showLoginLink">
    <a class="nav-link" routerLink="/register">Login / Register</a>
</li>
<li class="nav-item" *ngIf="userLogin">
    <a class="nav-link" routerLink="/register">{{userLogin}}</a>
</li>

After the navigation, the menu displays without the localStorage data causing *ngIf="userLogin" to not work. However, refreshing the page populates the values and activates the menu correctly. Is there a way to update the menu dynamically without a page refresh? (I prefer an explanation over a ready-made solution but I'm open to suggestions ^-^)

EDIT: Additional code provided based on comments :) Will make further edits if necessary

Answer №1

The issue occurs when you navigate to the home component and the "menu" component loads before the login component.

To address this, you can utilize an observer to inform the menu component that a login has been performed.

Begin by creating a service, let's name it AuthService

@Injectable()
export class AuthService {
  public loginPerformedObserver: Subject<any> = new Subject<any>();
}

Next, in your login component

    export class LoginComponent implements OnInit {

    formData;

    constructor(private usersService: UsersService, private router: 
    Router,private authService: AuthService) 
    { 
    }

    ngOnInit() {
    this.formData = new FormGroup({
      login: new FormControl('', [Validators.required]),
      password: new FormControl('', [Validators.required])
     });
    }

    onClickSubmit(data) {
      this.usersService.login(data).subscribe((response) => {
      const token = response.token;
      const userLogin = response.user.userLogin;

      localStorage.setItem('token', token);
      localStorage.setItem('login', userLogin);
      this.authService.loginPerformedObserver.next();
      this.router.navigate(['/home']);
      });
      }
}

Then, in your menu component

export class MenuComponent implements OnInit {
  showLoginLink = true;
  userLogin;
  private _loginPerformed: Subject<any>;

  constructor(private authService: AuthService) { }

  ngOnInit() {

    this._loginPerformed= this.authService.loginPerformedObserver;
    this._loginPerformed.subscribe(
     value => 
        {
      this.userLogin = localStorage.getItem('login');
      if (this.userLogin !== null) 
      {
        this.showLoginLink = false;
      }
        });

  }

}

For more information, please refer to this link

Answer №2

The occurrence is a result of the fact that ngOnInit was executed prior to the triggering of onClickSubmit. As a consequence, the value of userLogin remains unchanged.

To address this issue, it is essential to find a method to inform MenuComponent that the value of userLogin has been updated.

Answer №3

The reason behind this issue is the delay in writing data to the local storage, causing your navigation to occur before the data is ready for retrieval. To prevent this, you have a couple of options - either set a timeout before redirecting to the '/home' route or include attributes within the usersService to ensure that values are accessible immediately after a successful login.

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

In Angular, there is an issue where the @ViewChild decorator does not reflect changes when the value of the child component is updated within the

Does anyone know why the console.log("My status :", status); is not displaying after the child property has changed? Here is my child component code: @Output() public isLoggedIn: Subject<boolean> = new Subject(); constructor( private auth ...

Convert an interface type variable to a string representation

I'm just starting to learn angular and I'm attempting to convert my response from an interface type into a string so that I can store it in session storage. However, when I try to save them, they are being stored as [object] in the session storag ...

I am encountering an issue where my TSX import is being declared but not read when I attempt to pass it to the Jest render method. Can anyone explain

My Jest test file includes a simple import of a TSX component in my nextjs 13 project. However, when I try to use the component as a TSX tag, an error occurs: "'Properties' refers to a value, but is being used as a type here. Did you mean ...

Angular: Unable to modify the bound variable value in a directive from a transcluded child of another directive with a shared scope

I apologize if the title of this question seems complicated, but it's actually quite complex. Let me explain the situation: I have a directive with scope: false, transclude: true (Let's call this Directive1) Directive1's template refere ...

What allows mapped types to yield primitive outputs when using {[P in keyof T]}?

Check out this innovative mapped type example that utilizes the power of keyof: type Identity<T> = { [P in keyof T]: T[P]; }; Have you ever wondered why Identity<number> results in the primitive number type, rather than an object type? Is th ...

The request to DELETE the employee on the server at http://localhost:3000/employees/$%7Bid%7D could not be processed because it was not found

removeEmployee(id: number): Observable<any> { return this._http.delete('http://localhost:3000/staff/${id}'); } } error:HttpErrorResponse {headers: HttpHeaders, status: 404, statusText: 'Not Found', url: 'http://localhost:30 ...

How can I store the status of checked and unchecked checkboxes in an array of objects using Angular 7?

I have a set of checkboxes with a parent-child structure, and their values are generated dynamically in a loop. When I click the submit button, I want to capture the selected or unselected values in the specified format (as shown in the commented output) ...

Unable to crop the content of an input text field using Angular Forms 7

I am currently using Angular forms(7.2.10) with Parent / child components in my Angular 7 application. My goal is to successfully trim the text field input (String) from both leading and trailing white spaces, but unfortunately, I have not been able to ach ...

The CSS property object-fit: cover is unable to properly display JPEG images with EXIF orientation greater than 1

I'm having trouble with my Angular app and creating a gallery of photos from my Samsung Galaxy. I am using the "object-fit: cover" css attribute for a nice design, but it only seems to work correctly when the image has an EXIF "orientation" property e ...

There was an unexpected error: The development server's address information is not defined

Whenever I attempt to utilize ng serve with SSL and a custom hostname, I encounter the error message "An unhandled exception occurred: Dev-server address info is not defined." Has anyone found a solution to this issue? Here is the command I am using: ng s ...

Issues with FormArrays containing nested FormGroups

In my reactive form, I am using a FormArray to store FormGroup elements. Despite researching and trying various approaches, I still cannot determine why it is not functioning correctly. I have combined two simplified examples based on documentation and on ...

How can I adjust the column width in OfficeGen?

Currently, I am utilizing officeGen for the purpose of generating word documents. <sup> let table = [ [ { val: "TT", fontFamily: "Times New Roman", }, { val: "Ten hang", ...

The object may be null even after being enclosed in an if statement

In my Vue component, I have implemented the following method: dataURLtoBlob(dataurl: string): Blob { const arr: string[] = dataurl.split(","); if (arr) { if (arr[0]) { const mime = arr[0].match(/:(.*?);/)[1]; ...

Exploring TypeScript Compiler Options for ensuring code compliance beyond the confines of strict mode

Our goal is to set up TypeScript Compiler (TSC) with a command line option that can identify errors when developers declare class fields using implicit type expressions instead of explicit ones as illustrated below. class Appliance { //Desired coding ...

Endpoint path for reverse matching in Mongodb API

I am currently in the process of setting up a webhook system that allows users to connect to any method on my express server by specifying a method and an endpoint to listen to (for example PUT /movies/*). This setup will then send the updated movie to the ...

Advantages of creating model classes in Angular 2 and above

When developing a service for my domain, I discovered that I could easily implement the service using any type like this: list(): Observable<any> { const url = this.appUrlApi + this.serviceUrlApi; return this.http.get(url, { headers: this.he ...

Utilizing Angular and ASP .Net Web Api in conjunction with Plesk hosting platform

I have successfully created a website using Angular and .NET Framework 5.0. I was able to publish the Angular portion on Plesk and it is working correctly, but I am facing challenges in publishing the .NET Framework app and connecting it with Angular. ...

I want to modify a class in Angular 8 by selecting an element using its ID and adjust the styling of a div

Is it possible to dynamically add and remove classes using getElementById in Angular 8? I need to switch from 'col-md-12' to 'col-md-6' when a user clicks on the details icon. I also want to modify the style of another div by setting d ...

Equalizing Lists in Angular 2

Struggling to locate documentation on this topic, but in Angular 1 it was possible to achieve the following: <textarea ng-model="name" ng-list=","></textarea> With this setup, if you were to input "Hello, world!" into the textarea, the name v ...

Troublesome Deployment of Angular on IIS 7.5: Why It's Not Going

My Angular project deployment is experiencing issues with a missing component. Workflow Step 1: I ran the following command: ng-build --prod Step 2: I added the website to IIS using the path inside dist. Step 3: The index page functions correctly, ...