首页 > 解决方案 > 在函数上调用订阅时无法读取未定义的属性“订阅”

问题描述

我正在使用 jwt 令牌,我需要知道用户的角色来决定客户端允许哪些路由以及我在导航栏中显示哪个菜单。

所有这些都保存在一个服务中,该服务应该告诉 app-routing 模块用户是否被允许访问此路由,以及导航栏是否应该显示菜单。

这是有效的现有解决方案:

安全服务.ts

import { ApplicationRef, EventEmitter, Injectable, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from "rxjs";
import { ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";
import { LoggingUtil } from "../utils/logging.util";
import { ServerUtil } from "../utils/server.util";
import { UserRoleModel } from "../model/models-generated";
import { Log } from "@angular/core/testing/src/logger";

@Injectable({
  providedIn: 'root'
})
export class SecurityService {

  constructor(private client: HttpClient) {}

  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    if (next.data.permission.includes(SecurityService.AVA_PERMISSION)) {
      this.getUserRoles().subscribe((result) => {
        return of(result.hasAvaRole);
      });
    } else if (next.data.permission.includes(SecurityService.ADMIN_PERMISSION)) {
      this.getUserRoles().subscribe((result) => {
        return of(result.hasAdminRole);
      });
    } else if (next.data.permission.includes(SecurityService.USER_PERMISSION)) {
      this.getUserRoles().subscribe((result) => {
        return of(result.hasUserRole);
      });
    }
    return of(false);
  }



  public getUserRoles(): Observable<UserRoleModel> {
    let serverUrl = ServerUtil.GetRestApiURL() + '/role';
    return this.client.get<UserRoleModel>(serverUrl);
  }


navbar.component.ts

import {Component, OnInit} from '@angular/core';
import {SecurityService} from "../../services/security.service";
import {Observable} from "rxjs";


@Component({
  selector: 'app-navbar',
  templateUrl: "./navbar.component.html",
  styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
  isAdmin: boolean;

  constructor(private securityService: SecurityService) {}


  ngOnInit() { 
    this.securityService.getUserRoles().subscribe(value => {
      this.isAdmin = value.hasAdminRole;
    })
  }
}


这个版本的问题是canActivate()将在每次路由更改时调用,因此也会调用getUserRoles()

我的目标是做到这一点,你既可以用旧方式做,也可以存储价值。切换是在弹簧轮廓的帮助下完成的,因此我将代码更改如下:

安全服务.ts

export class SecurityService {
  hasAvaRole = false;
  hasAdminRole = false;
  hasUserRole = false;
  profiles: string[] = [];
  profileLoaded = false;

  // previously getUserRoles
  loadRoles(): Observable<UserRoleModel> {
    let serverUrl = ServerUtil.GetRestApiURL() + '/' + this.ACTION_PATH;
    LoggingUtil.debug("posting to remote server : " + serverUrl);
    return this.client.get<UserRoleModel>(serverUrl);
  }

  loadProfiles(): Observable<string[]> {
    let serverUrl = ServerUtil.GetRestApiURL() + '/' + this.PROFILE_PATH;
    LoggingUtil.debug("calling remote server : " + serverUrl);
    LoggingUtil.debug('client info :');
    console.log(this.client);
    return this.client.post<string[]>(serverUrl, null);
  }

  private getUserRolesSync(): UserRoleModel {
    return {'hasAvaRole': this.hasAvaRole, 'hasAdminRole': this.hasAdminRole, 'hasUserRole': this.hasUserRole}
  }


  getUserRoles(): Observable<UserRoleModel> {
     // if roles aren't in memory we load them
    if (!this.profileLoaded) {
      this.loadProfiles().subscribe((profiles: string[]) => {
        this.profiles = profiles;
        this.profileLoaded = true;
        if (this.profiles.includes('saving-role')) {
          this.loadRoles().subscribe(result => {
            this.hasAvaRole = result.hasAvaRole;
            this.hasAdminRole = result.hasAdminRole;
            this.hasUserRole = result.hasUserRole;
            return of(this.getUserRolesSync());
          });
        } else {
          return this.loadRoles();
        }
      });
    } else {
      if (this.profiles.includes('saving-role')) {
        return of(this.getUserRolesSync());
      } else {
        return this.loadRoles();
      }
    }

起初我以为 httpClient 没有被注入,但通过打印它我发现事实并非如此。弹簧型材也已正确加载。

我现在在订阅函数的行的 navbar.component.ts 中收到错误“无法读取未定义的属性‘订阅’”

标签: angularrxjs

解决方案


问题在这里getUserRoles。如果用户进入第一个if条件,则控件将进入subscribe块内。但是你没有从那里返回任何东西。所以你必须return从那里开始。而不是subscribeing,您必须将上下文切换到内部 Observable:pipeswitchMap

getUserRoles(): Observable < UserRoleModel > {
  // if roles aren't in memory we load them
  if (!this.profileLoaded) {
    return this.loadProfiles().pipe(
      switchMap(profiles: string[]) => {
        this.profiles = profiles;
        this.profileLoaded = true;
        if (this.profiles.includes('saving-role')) {
          this.loadRoles().pipe(
            switchMap(result => {
              this.hasAvaRole = result.hasAvaRole;
              this.hasAdminRole = result.hasAdminRole;
              this.hasUserRole = result.hasUserRole;
              return of(this.getUserRolesSync());
            })
          );
        } else {
          return this.loadRoles();
        }
      })
    );
  }
  else {
    if (this.profiles.includes('saving-role')) {
      return of(this.getUserRolesSync());
    } else {
      return this.loadRoles();
    }
  }
}

推荐阅读