Angular 2/4 - 在前端存储当前用户对象详细信息(而不是向后端发送连续的HTTP请求)

ToD*_*oDo 2 typescript angular

我想要一种有效的方法来存储(和更新)前端中的当前用户详细信息,而不是在加载新组件时向后端发出新的HTTP GET请求.

我添加了一个UserService类,它在调用类中的方法时设置/更新currentUser对象.

import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';

@Injectable()
export class UserService {

    public currentUser: any;

  constructor(private http: Http) { }

  updateCurrentUser() {
    this.http.get('api/login/currentUser').subscribe(data => {
        this.currentUser = data;
    });
  }

}
Run Code Online (Sandbox Code Playgroud)

我发现这种方法会导致竞争条件问题.例如,如果currentUser.username在概要文件组件中请求但服务类尚未完成请求,则会发生错误.

我也尝试检查是否currentUser未定义然后调用updateCurrentUser()但它似乎不起作用.

if(this.userService.currentUser === undefined){
    this.userService.updateCurrentUser();
} 
Run Code Online (Sandbox Code Playgroud)


更新:浏览器控制台中显示的错误消息 在此输入图像描述


使用的HTML更新:
我使用数据绑定来显示用户名(直接从UserService类调用它).例如

<span class="username-block">{{userService.currentUser.username}}</span>
Run Code Online (Sandbox Code Playgroud)

我也尝试将currentUser对象分配给我试图显示用户名的组件中的变量,但它具有相同的结果.


我将不胜感激任何帮助/反馈.

Ric*_*sen 9

您可以在html中尝试Elvis /存在运算符(我认为这是正确的名称).currentUser之后的问号表示如果currentUser未定义,请不要试图评估用户名.

假设您的异步代码最终会填充它,页面应该显示它.

<span class="username-block">{{userService.currentUser?.username}}</span>
Run Code Online (Sandbox Code Playgroud)

编辑

我刚刚注意到你正在输出userService .currentUser.username.您可能希望将服务中的observable返回到组件,以便数据具有反应性.就像是,

updateCurrentUser() {
    return this.http.get('api/login/currentUser')
      .catch(error => this.handleError(error, 'currentUser'));
}
Run Code Online (Sandbox Code Playgroud)

然后在组件中,

private currentUser;

ngOnInit() {
  this.userService.updateCurrentUser()
    .take(1)
    .subscribe(data => { this.currentUser = data });
}
Run Code Online (Sandbox Code Playgroud)

并且html现在引用本地组件副本,最初未定义但最终由订阅填充.注意,take(1)是关闭订阅并避免内存泄漏.

    <span class="username-block">{{currentUser?.username}}</span>
Run Code Online (Sandbox Code Playgroud)

编辑#2

道歉,你的问题是关于避免多个HTTP调用.
在这种情况下,服务代码应该是

export class UserService {

  private currentUser: any; // private because only want to access through getCurrentUser()

  constructor(private http: Http) { }

  getCurrentUser() {
    return this.currentUser
      ? Observable.of(this.currentUser) // wrap cached value for consistent return value
      : this.http.get('api/login/currentUser')
          .do(data => { this.currentUser = data }) // cache it for next call
          .catch(error => this.handleError(error, 'currentUser'));
  }
Run Code Online (Sandbox Code Playgroud)


Deb*_*ahK 7

你在使用路由吗?如果是这样,您可以在第一条路线上设置需要用户数据的路线解析器.然后,解析器始终等待显示视图,直到检索到解析器数据.

例如,这是我的一个路线解析器.请注意,它调用服务来检索数据.

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

import { Observable } from 'rxjs/Observable';

import { IMovie } from './movie';
import { MovieService } from './movie.service';

@Injectable()
export class MovieResolver implements Resolve<IMovie> {

    constructor(private movieService: MovieService) { }

    resolve(route: ActivatedRouteSnapshot,
            state: RouterStateSnapshot): Observable<IMovie> {
        const id = route.paramMap.get('id');
        return this.movieService.getMovie(+id);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后将解析器添加到适当的路径,如下所示:

  {
    path: ':id',
    resolve: { movie: MovieResolver },
    component: MovieDetailComponent
  },
Run Code Online (Sandbox Code Playgroud)

在检索数据之前,不会显示该路线.

  • 这个解决方案与上面的 Elvis/存在主义建议不同,它允许我们避免将任何内部类方法暴露给组件 HTML,并且在实现预期效果的同时似乎通常更加解耦。 (2认同)