为什么我的用户ID在传递到我的URL时未定义?

时间:2017-04-04 21:24:00

标签: javascript angular typescript

我正在构建一个配置文件页面,并试图让经过身份验证的用户数据显示在那里。我的API调用与他们的id一起工作,如果我手动将id输入到url中,它在前端工作。

但是当我尝试从导航栏导航到配置文件时,我收到了

400 Bad Request for URL: http://localhost:3000/users/undefined

我现在可以假设它是一个异步问题。我的个人资料页面调用用户数据,但该用户数据在我的导航组件中不可用。如果我想正确导航,似乎我需要将我的id param传入我的个人资料[routerLink]。由于我的用户数据在我的导航组件中不可用,因此无法通过。

有更好的方法吗?我应该使用事件发射器吗?

Angular的新手 - 非常感谢!

配置文件组件

import { Component, OnInit, Input } from '@angular/core';
import { AuthService } from '.././services/auth.service';
import { UserService } from '.././services/user.service'
import { Router, ActivatedRoute } from '@angular/router';


@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css'],
  providers: [UserService]
})
export class ProfileComponent implements OnInit {

  currentUser;

  isAuth: boolean;

  constructor(
    private session: AuthService,
    private router:  Router,
    private userService: UserService,
    private route: ActivatedRoute

  ) {


  this.session.isAuth
       .subscribe((isAuth: boolean) => {
       // user will be false if logged out
       // or user object if logged in.
         this.isAuth = isAuth;
       });
   if (this.session.token) {
     this.isAuth = true;
     console.log(this.session);
   } else {
     this.isAuth = false;
   }

  }




  ngOnInit() {

    this.route.params.subscribe(params => {
      this.getUserDetails(params['id']);
    });


  }

  getUserDetails(id) {
     this.userService.get(id)
       .subscribe((user) => {
         this.currentUser = user;
         console.log(this.currentUser);
       });
   }


  }

导航模板

我正在导航到我的个人资料页面。

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">bnb</a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav navbar-right">


        <li *ngIf="!isAuth"><a [routerLink]="['login']">Login</a></li>
        <li *ngIf="isAuth"><a [routerLink]="['profile']"><span class="glyphicon glyphicon-user" aria-hidden="true"></span>&nbsp;&nbsp; Profile</a></li>
        <li *ngIf="isAuth"><a (click)="logout()">Logout</a></li>
        <li *ngIf="!isAuth"><a [routerLink]="['signup']">Signup</a></li>

      </ul>
    </div>
  </div>
</nav>

导航组件

import { Component, OnInit, Input } from '@angular/core';
import { AuthService } from '.././services/auth.service';
import { UserService } from '.././services/user.service';
import { Router, ActivatedRoute } from '@angular/router';

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

  isAuth: boolean;

  currentUser: any;


  constructor(
    private session: AuthService,
    private userService: UserService,
    private router: Router,
    private route: ActivatedRoute
  ) {

    this.currentUser = JSON.parse(localStorage.getItem("User"))
    console.log("USER",this.currentUser)                       //Currently returns Null
    console.log(this.session)
    this.session.isAuth
      .subscribe((isAuth: boolean) => {
      // user will be false if logged out
      // or user object if logged in.
        this.isAuth = isAuth;
      });
  if (this.session.token) {
    this.isAuth = true;
  } else {
    this.isAuth = false;
  }
}

  ngOnInit() {

  }


  logout() {
   this.session.logout();

 }




}

路由器

import { Routes } from '@angular/router';

import { LoginComponent } from '../login/login.component';
import { SignupComponent } from '../signup/signup.component';
import { HomeComponent } from '../home/home.component';
import { RentalListingsComponent } from '../rental-listings/rental-listings.component';
import { SingleRentalComponent } from '../rental-listings/single-rental/single-rental.component';
import { ProfileComponent } from '../profile/profile.component'
import { AuthService } from '../services/auth.service';


export const routes: Routes = [
    { path: '', component: HomeComponent },
    { path: 'login', component: LoginComponent },
    { path: 'signup', component: SignupComponent },
    { path: 'rentals', component: RentalListingsComponent },
    { path: 'listing', component: SingleRentalComponent },
    { path: 'profile/:id', component: ProfileComponent, canActivate: [AuthService] }   <--profile path.  I know I have to match my url paths, but don't know how to do this from the navbar.  
    // { path: 'home', component: HomeComponent, canActivate: [AuthService] },

    { path: '**', redirectTo: '' }
];

2 个答案:

答案 0 :(得分:1)

感谢您提供详细信息。在某个地方,您需要订阅“登录后”或“身份验证”事件,获取用户配置文件JSON,并将其保存到localstorage,以便您可以在任何地方使用它。如果您无法挂钩或订阅其中一个,那么请在代码中方便的地方执行。找出您可以进行哪些调用以获取整个用户JSON并将其保存如下...

查看下面的AuthService init()。第一行是this.authProvider.on('authenticated', this.onAuth);。无论您使用何种身份验证服务API,都应该为您提供一种方法,以便在有人登录时指定回调(提供登录令牌)。onAuth回调函数将令牌保存在localstorage中,然后fetchProfile(...){...}生成另一个调用身份验证服务API以使用刚刚收到的令牌this.user.getProfile(idToken, this.onProfile);获取整个JSON用户配置文件。例如,我在项目中使用Auth0,而我对Auth0 API的调用看起来像this.lock.getProfile(idToken, this.onProfile);,但我将其替换为您的调用可能类似的示例this.user.getProfile(idToken, this.onProfile);因此,请使用您在API中使用的任何内容替换fetchProfile 。然后onProfile回调使用this.localStorage.set('profile', profile);将整个JSON配置文件保存在本地存储中的单个密钥中。然后,您可以通过调用this.localStorage.get('profile')随时获取它。

  1. 不要通过延迟加载的ProfileComponent提供UserService。这会在您可能不需要的依赖项注入树上创建一个单独的分支。请参阅https://angular-2-training-book.rangle.io/handout/modules/shared-modules-di.html在顶级模块中导入UserService,如AppModule或SharedModule,并在那里提供。如果它在AppModule中,则无需导出。
  2. app.module.ts

    ...
    @NgModule({
      imports: [
        ...
        UserService,
        ...
        ]
      providers: [
        ...
        UserService,
        ...
        ]
    
    1. 在Auth中处理与Auth相关的内容,而不是Profile。配置文件似乎是视觉/实现特定的(例如,它有一个模板)。这是一个代码段示例。
    2. auth.service.ts

      @Injectable()
      export class Auth {
      
        userProfile: UserProfile;
      
        constructor(
                    ...
                    private localStorage: LocalStorageService,
                    private router: Router,
                    private user: UserService,
                    private authProvider: ...
                    ...
                    ) {
        }
      
        init() { 
            this.authProvider.on('authenticated', this.onAuth);
            // Set userProfile attribute if already saved profile
            this.userProfile = this.localStorage.get('profile');
            setTimeout(() => { // let AppComponent listener initialize
              this.localStorage.set('profile', this.userProfile);
            }, 0);
          }
        }
      
        onAuth = (authResult: AuthResult) => {
          this.localStorage.set('id_token', authResult.idToken);
          this.fetchProfile(authResult.idToken);
        }
      
        // Save current route for redirect url
        login() {
          this.localStorage.set('redirect_url', this.router.url);
          this.authProvider.show({initialScreen: 'login'});
        };
      
        // Check if user is logged in.
        authenticated() {
          // Check if unexpired token. 
          // Searches for item in localStorage with key == 'id_token'
          return this.authProvider.tokenNotExpired();
        };
      
        logout() {
          this.router.navigateByUrl('');
          this.userProfile = undefined; // do before localstorage
          this.localStorage.remove('id_token');
          this.localStorage.remove('profile');
        };
      
        fetchProfile(idToken: string) {
          this.user.getProfile(idToken, this.onProfile);
        }
      
        /**
         * On profile event callback.
         * Save profile to LocalStorage. 
         * Redirect to url if present in LocalStorage.
         */
        onProfile = (error: any, profile: UserProfile) => {
          if (error) {
            console.log(error);
            return;
          }
          this.userProfile = profile; 
          this.localStorage.set('profile', profile);
      
          // Redirect if there is a saved url to do so.
          const redirectUrl: string = this.localStorage.get('redirect_url');
          if (redirectUrl !== undefined) {
            this.router.navigateByUrl(redirectUrl);
            this.localStorage.remove('redirect_url');
          }
        }
      
      1. 通过LocalStorageService与localStorage交互,并按如下方式订阅更改。
      2. localstorage.service.ts

        import { Injectable } from '@angular/core';
        import { Subject }    from 'rxjs/Subject';
        
        @Injectable()
        export class LocalStorageService {
          [key:string]: any;
        
          /**
           * define your localstorage variables here as observables
           */
          id_token$ = new Subject();
          redirect_url$ = new Subject();
          profile$ = new Subject();
          customer$ = new Subject();
        
          set(key: string, value: any) {
            this[key + '$'].next(value); // this will make sure to tell every subscriber about the change.
            localStorage.setItem(key, JSON.stringify(value));
          }
        
          get(key: string) {
            const value = localStorage.getItem(key);
            return value && JSON.parse(value);
          }
        
          remove(key: string) {
            this[key + '$'].next(undefined);
            localStorage.removeItem(key);
          }
        }
        
        1. 在构造函数中不要这么做。例如:
        2. app.component.ts

           export class AppComponent implements OnDestroy, OnInit {
          
          
              webRobot: boolean = false;
          
              private profileSub: any;
              private customerSub: any;
              private subscriptionSub: any;
          
              constructor(
                    private analyticsService: AnalyticsService,
                    private auth: Auth,
                    private localStorage: LocalStorageService,
                    ) {
                    }
          
              ngOnInit(): void {
                   this.init();
              }
          
              init() {
                  this.auth.init(this.webRobot);
                  this.analytics.init(this.webRobot);
                  if (!this.webRobot) {
                    // subscribe to authed profile changes
                    this.profileSub = 
                             this.localStorage.profile$.subscribe(this.onProfile);
                    // Subscribe to changes to Stripe customer
                    this.customerSub = 
                             this.localStorage.customer$.subscribe(this.onCustomer);
                }
          
          
            // always delete active subscribes on destroy
            ngOnDestroy() {
              this.profileSub.unsubscribe();
              this.customerSub.unsubscribe();
            }
          
            onProfile = (profile: UserProfile) => { 
          
            // ...do stuff        
          
            }
          
            onCustomer= (profile: Customer) => {
          
            // ...do stuff
          
            }
          

答案 1 :(得分:0)

在您的个人资料路由配置中,它需要id查询参数

 { path: 'profile/:id', component: ProfileComponent, canActivate: [AuthService] }   
<--profile path.  
 I know I have to match my url paths, 
 but don't know how to do this from the navbar. 

但您的导航栏链接未传递ID值

 <li *ngIf="isAuth"><a [routerLink]="['profile']"><span class="glyphic

你需要在导航栏中做这样的事情

 <li *ngIf="isAuth"><a [routerLink]="['profile/user.id']">