模型更改时视图未更新

时间:2018-02-02 09:04:51

标签: angular typescript rxjs

我有一个Angular(5.2.3)应用程序,我想在页面的右上角显示一个登录/注销按钮。在幕后,我尝试使用外部Open ID Connect提供程序以静默方式登录用户。当回调到来时,我想显示用户的名字和注销按钮。但是,唉,视图永远不会更新以反映这一点。

这是组件的视图:

<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>
我已经尝试了各种方法来解决这个问题,基于StackOverflow上的各种问题。以下是该组件现在的样子:

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() {}
}
所有console.log调用都按预期执行,只有视图永远不会更新。

5 个答案:

答案 0 :(得分:1)

你的问题与另一个问题类似,我过去曾回答过这个问题。

Trigger update of component view from service - No Provider for 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.
    }
  }
}

In this documentation page you can find out more about ApplicationRef

Live example - here I am using AppRef to trigger view update when client subscribes to push on Safari browser.

您也可以尝试在this.applicationRef.tick();内移动this.zone.run(()=>{});(如果您确实需要NgZone)。

答案 1 :(得分:0)

这是我的建议,我将使用observables。

在我的服务中:

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) {}

  ...
}

答案 2 :(得分: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() {}
}

希望这有帮助。

答案 3 :(得分:0)

我似乎无法找到您在此处提供的代码的任何问题。我能想到的是与ngIf指令的某种冲突。 如果所有控制台日志都符合预期,我可以建议您在开始时初始化loggedIn值,然后尝试在Html文件中使用*ngIF else模板。

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提供代码。希望我能帮忙。

答案 4 :(得分: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() {}
}

我希望这可以解决您关于视图更新的问题。