View does not refresh when model changes

I have an Angular application (5.2.3) in which I want to display the login / logout button in the upper right corner of the page. Behind the scenes, I'm trying to log in logically using an external Open ID Connect provider. When the callback arrives, I want to display the username and exit button. But alas, the view is never updated to reflect this.

Here is the component view:

<ul class="navbar-nav">
  <li class="nav-item" *ngIf="!loggedIn">
    <a href="#" class="nav-link" (click)="login()">Login</a>
  </li>
  <li class="nav-item" *ngIf="loggedIn">
    <a href="#" class="nav-link disabled" disabled>Hello, {{ name }}</a>
  </li>
  <li class="nav-item" *ngIf="loggedIn">
    <a href="#" class="nav-link" (click)="logoff()">Logout</a>
  </li>
</ul>
Run codeHide result
I tried to use different approaches to solving the problem, based on various StackOverflow questions. Here's what the component looks like now:

import {
  Component,
  OnInit,
  SimpleChanges,
  ApplicationRef,
  ChangeDetectorRef,
  NgZone
} from '@angular/core';
import {
  OAuthService
} from 'angular-oauth2-oidc';
import {
  SessionService
} from '../session.service';

@Component({
  selector: 'app-navigation',
  templateUrl: './navigation.component.html',
  styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit {
  name: string;
  loggedIn: boolean;

  constructor(private oauthService: OAuthService,
    private sessionService: SessionService,
    private applicationRef: ApplicationRef,
    private zone: NgZone,
    private cd: ChangeDetectorRef) {
    //this.loggedIn = false;
  }

  ngOnInit() {
    this.sessionService.state.subscribe(isLoggedIn => {
      console.log('Detected a state change! User is logged in: ' + isLoggedIn);
      this.zone.run(() => {
        if (!isLoggedIn) {
          this.name = null;
          this.loggedIn = false;
          console.log('User not logged in. Name is set to null');
        } else {
          const claims: any = this.oauthService.getIdentityClaims();
          console.log(`Got claims. Name is now ${claims.name}`);
          this.name = claims.name;
          this.loggedIn = true;
        }
      });
      this.cd.detectChanges();
      this.applicationRef.tick();
    });
    this.sessionService.configureWithNewConfigApi();
  }

  public login() {}

  public logoff() {}
}
Run codeHide result
All calls console.logare made as expected, only the view is never updated.
+4
source share
6 answers

, .

- ChangeDetectorRef

, Angular . ApplicationRef. :

import {Injectable, ApplicationRef } from '@angular/core';

@Injectable()
export class UserService {
  private isLoggedIn: boolean = false;
  constructor(private ref: ApplicationRef) {}

  checkLogin() {
    let stillLogged = //some logic making API calls, or anything other.
    if (this.isLoggedIn != stillLogged) {
        this.isLoggedIn = stillLogged;
        this.ref.tick(); //trigger Angular to update view.
    }
  }
}

ApplicationRef

Live - AppRef , Safari.

this.applicationRef.tick(); this.zone.run(()=>{}); ( NgZone).

+1

, .

:

export class MyLoginService {
  loginSubject: ReplaySubject<any>;
  login$: Observable<any>;

  constructor(private http: HttpClient) {
    this.loginSubject = new ReplaySubject<any>();
    this.login$ = this.loginSubject.asObservable();
  }

  login(): void {
    // do your login logic and once is done successfully
    this.loginSubject.next({loggedIn: true});
  }
}

html:

<ul class="navbar-nav">
  <li class="nav-item" *ngIf="!(myLoginSrv.login$ | async)?.loggedIn">
    <a href="#" class="nav-link" (click)="login()">Login</a>
  </li>
  <li class="nav-item" *ngIf="(myLoginSrv.login$ | async)?.loggedIn">
    <a href="#" class="nav-link disabled" disabled>Hello, {{ name }}</a>
  </li>
  <li class="nav-item" *ngIf="(myLoginSrv.login$ | async)?.loggedIn">
    <a href="#" class="nav-link" (click)="logoff()">Logout</a>
  </li>
</ul>

:

@Component({
  ...
})
export class MyComponent {
  constructor(public myLoginSrv: MyLoginService) {}

  ...
}
0

, , , , .. , , - . ? detectChanges(). , ...

, subscribe() , markForCheck()... , , .

@Component({
  selector: 'app-navigation',
  templateUrl: './navigation.component.html',
  styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit {
  name: string;
  loggedIn: boolean;

  constructor(private oauthService: OAuthService,
    private sessionService: SessionService,
    private applicationRef: ApplicationRef,
    private zone: NgZone,
    private cd: ChangeDetectorRef) {
    //this.loggedIn = false;
  }

  ngOnInit() {
    this.sessionService.state.subscribe(isLoggedIn => {
      console.log('Detected a state change! User is logged in: ' + isLoggedIn);
      if (!isLoggedIn) {
        this.name = null;
        this.loggedIn = false;
        console.log('User not logged in. Name is set to null');
      } else {
        const claims: any = this.oauthService.getIdentityClaims();
        console.log(`Got claims. Name is now ${claims.name}`);
        this.name = claims.name;
        this.loggedIn = true;
      }
      this.cd.detectChanges();
    });
    this.sessionService.configureWithNewConfigApi();
  }

  public login() {}

  public logoff() {}
}
Hide result

, .

0

- , . , , - - ngIf. , , loggedIn *ngIF else HTML.

loggedIn: boolean = false;

html:

<ul class="navbar-nav">
    <li class="nav-item" *ngIf="!loggedIn; else loggedInTemplate">
        <a href="#" class="nav-link" (click)="login()">Login</a>
    </li>
    <ng-template #loggedInTemplate>
        <li class="nav-item">
          <a href="#" class="nav-link disabled" disabled>Hello, {{ name }}</a>
        </li>
        <li class="nav-item">
          <a href="#" class="nav-link" (click)="logoff()">Logout</a>
        </li>
    </ng-template>
  </ul>

, -. , sessionService . , .

0

ngZone, . :

this.ngZone.run(() => {
  this.loggedIn = true;
});

, :

import {
Component,
OnInit,
SimpleChanges,
ApplicationRef,
ChangeDetectorRef,
NgZone } from '@angular/core';
import {  OAuthService} from 'angular-oauth2-oidc';
import { SessionService } from '../session.service';

@Component({
 selector: 'app-navigation',
 templateUrl: './navigation.component.html',
 styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit {
  name: string;
  loggedIn: boolean;

  constructor(private oauthService: OAuthService,
    private sessionService: SessionService,
    private zone: NgZone) {
  }

  ngOnInit() {
    this.sessionService.state.subscribe(isLoggedIn => {
      console.log('Detected a state change! User is logged in: ' + isLoggedIn);

        if (!isLoggedIn) {
          this.name = null;
          this.loggedIn = false;
          console.log('User not logged in. Name is set to null');
        } else {
          const claims: any = this.oauthService.getIdentityClaims();
          console.log(`Got claims. Name is now ${claims.name}`);
          this.name = claims.name;
          this.zone.run(() => {
          this.loggedIn = true;
          });
        }
    });
    this.sessionService.configureWithNewConfigApi();
  }

  public login() {}

  public logoff() {}
}

, , .

0

Angular NgZone :

Angular.

, , , , Angular. runOutsideAngular , , Angular run.

this.loggedIn = true; this.loggedIn = false; zone.

, detectChanges() tick() , loggedIn .

0
source

Source: https://habr.com/ru/post/1693067/


All Articles