Angular 路由器导航在之前的代码行之前执行

Angular router navigate perform before previous code lines

我的 angular 代码遇到了一个奇怪的问题。 我有一个调用 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>

我注意到 this.router.navigate() 在我的 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>

执行导航时,会显示菜单,但 localStorage 为空,因此我的 userLogin 为空,我的 *ngIf="userLogin" 不起作用。重定向后立即填充 localStorage,如果我刷新页面,我有我的值并且我的菜单有效(显示我的第二个 <li> 而不是第一个)。

这是标准行为吗?如何在不刷新页面的情况下实现菜单更改? (我更喜欢解释而不是现成的解决方案,但我不拒绝解决方案^-^)

编辑:根据评论添加一些代码 :) 如果需要更多代码,我会再次编辑

这是因为 ngOnInitonClickSubmit 触发之前被调用了一次。所以 userLogin 不会更新。

您需要以某种方式通知 MenuComponent userLogin 值已经更改

发生这种情况是因为在本地存储中写入数据需要花费大量时间,因此您的导航是在数据可用并可以检索之前执行的。 您可以通过在导航到“/home”路由之前设置超时或通过在 usersService 中添加属性来避免这种情况,这样值将在成功登录后立即可用。

这是因为您的导航到主页组件和 "menu" 组件在登录组件之前加载。

您可以使用观察器让菜单组件知道登录已执行。

首先,您将创建一个服务,我们称它为 AuthService

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

然后在您的登录组件中

    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.loginPreformedObserver.next();
      this.router.navigate(['/home']);
      });
      }
}

然后在您的菜单组件中

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

  constructor(private authService: AuthService) { }

  ngOnInit() {

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

  }

}

更多详情请看这里link