ngIf ExpressionChangedAfterItHasBeenCheckedError

Я пытаюсь скрыть элемент входа в систему из меню панели навигации в моем приложении Angular 6. У меня есть AuthService, как показано ниже:

import { Injectable, Inject } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { JwtHelperService } from '@auth0/angular-jwt';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private url: string;
  private loggedIn = new BehaviorSubject<boolean>(false);
  isLoggedIn$ = this.loggedIn.asObservable();

  constructor(private http: HttpClient, private jwtHelper: JwtHelperService, @Inject('BASE_URL') baseUrl: string) {
    this.url = baseUrl + 'api/auth';
    this.loggedIn.next(this.isLoggedIn());
  }

  login(credentials) {
    const headers = new HttpHeaders({ 'Content-Type': 'application/json' });

    return this.http.post<any>(`${this.url}/login`, JSON.stringify(credentials), { headers: headers }).pipe(
      map(response => {
        if (response && response.token) {
          localStorage.setItem('token', response.token);
          this.loggedIn.next(true);

          return true;
        }        

        return false;
      })
    );
  }

  logout() {
    localStorage.removeItem('token');
    this.loggedIn.next(false);
  }

  isLoggedIn() {
    return !this.jwtHelper.isTokenExpired();
  }
}

Вот что у меня в NavMenuComponent:

export class NavMenuComponent implements OnInit, OnDestroy {
  isExpanded = false;
  isLoggedIn: boolean;
  authSubscription: Subscription;

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

  ngOnInit() {
    this.authSubscription = this.authService.isLoggedIn$
      .subscribe(loggedIn => this.isLoggedIn = loggedIn);
  }

  ngOnDestroy() {
    this.authSubscription.unsubscribe();
  }

  onLogout() {
    this.authService.logout();
    this.router.navigate(['/login']);
  }

  collapse() {
    this.isExpanded = false;
  }

  toggle() {
    this.isExpanded = !this.isExpanded;
  }
}

И вот HTML:

<li *ngIf="!isLoggedIn" class="nav-item" [routerLinkActive]="['link-active']">
  <a class="nav-link" [routerLink]="['/login']">Login</a>
</li>

Но когда я запускаю это, я получаю следующую ошибку:

Ошибка: ExpressionChangedAfterItHasBeenCheckedError: выражение было изменено после проверки. Предыдущее значение: 'ngIf: true'. Текущее значение: 'ngIf: false'.

Я читал об этой ошибке, но не понимал, как ее исправить. Любая помощь?


person ataravati    schedule 26.12.2019    source источник
comment
К сожалению, HTML не попал в публикацию, пожалуйста, обновите его.   -  person Alexander Leonov    schedule 27.12.2019
comment
@AlexanderLeonov Извините! Обновлено.   -  person ataravati    schedule 27.12.2019
comment
Эта ошибка появится только в вашей среде разработки. Если вы включите режим prod, который вы включите при развертывании кода, он исчезнет. Кстати, если вы используете async pipe напрямую, вам не придется управлять такими состояниями, как isLoggedIn, а angular также позаботится об отписке. Так что вам не нужно делать это. AuthSubscription.unsubscribe (); также   -  person Vatsal    schedule 27.12.2019


Ответы (3)


Я предполагаю, что это связано с тем, как вы устанавливаете начальное значение для loggedIn в конструкторе службы: this.loggedIn.next(this.isLoggedIn());

Попробуйте объявить переменную только в своем классе и сделать ее только new BehaviourSubject() в своем конструкторе.

Что-то типа:

 private url: string;
  private loggedIn: BehaviorSubject<boolean>;
  isLoggedIn$: Observable<boolean>;

  constructor(private http: HttpClient, private jwtHelper: JwtHelperService, @Inject('BASE_URL') baseUrl: string) {
    this.url = baseUrl + 'api/auth';
    this.loggedIn = new BehaviourSubject<boolean>(this.isLoggedIn());
    this.isLoggedIn$ = this.loggedIn.asObservable()
  }

Сообщите мне, если это что-то изменит

person Jojofoulk    schedule 26.12.2019
comment
Нет, это не помогло. - person ataravati; 27.12.2019

Вы пытаетесь использовать метод вместо свойства для возврата наблюдаемого в AuthService

isLoggedIn: BehaviorSubject<boolean> = new BehaviorSubject(false);

isLoggedIn$() {
    return this.isLoggedIn.asObservable();
}
person Jonathan Lopez    schedule 26.12.2019

вы можете решить эту ошибку с помощью setTimeout.

  export class NavMenuComponent implements OnInit, OnDestroy {
  isExpanded = false;
  isLoggedIn: boolean;
  authSubscription: Subscription;

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

  ngOnInit() {
   setTimeout(() => {
    this.authSubscription = this.authService.isLoggedIn$
      .subscribe(loggedIn => this.isLoggedIn = loggedIn);
   });
  }

  ngOnDestroy() {
    this.authSubscription.unsubscribe();
  }

  onLogout() {
    this.authService.logout();
    this.router.navigate(['/login']);
  }

  collapse() {
    this.isExpanded = false;
  }

  toggle() {
    this.isExpanded = !this.isExpanded;
  }
}
person Arunkumar Ramasamy    schedule 27.12.2019
comment
@ataravati можно ли создать эту задачу в stackblitz? - person Arunkumar Ramasamy; 27.12.2019