2017-04-04 19 views
0

我正在構建一個配置文件頁面,並嘗試獲取已驗證的用戶數據以顯示在那裏。我的API調用與他們的ID一起工作,如果我手動將該ID輸入到URL中,它將在前端工作。爲什麼我的用戶標識在傳遞到我的URL時未定義?

但是,當我嘗試導航從導航欄的個人主頁上,我收到一個

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

我現在就可以假設的是,這是一個不同步的問題。我的個人資料頁面會調用用戶數據,但該用戶數據在我的導航組件中不可用。如果我想正確導航,好像我需要將我的ID參數傳遞到我的配置文件[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: '' } 
]; 
+0

看你的代碼,我沒有看到既不在路由,也不在模板(HTML)代碼中定義的'users'路徑。 –

+0

如果我將它用於我的API調用,是否需要在Angular 2中定義該路徑?當我在Express中使用該路徑(使用實際用戶標識)時,它將返回JSON數據。我必須匹配前後的路徑嗎?我也收到用戶數據,如果我手動將ID放入我的配置文件(前端)路徑。 – universesurfer

+0

如果你想獲得一個查詢字符串,嘗試使用從'routerState'獲取id,就像這樣:'this.router.routerState.root.params.subscribe(data => console.log(data ['id ']));'如果你使用了一個參數(例如'serverUrl/profile/14'),那麼確保你的路由被格式化爲'this.router.navigate(['/ profile',id]); '(不是查詢字符串參數)。 –

回答

1

感謝。某處您需要訂閱「登錄後」或「身份驗證」事件,請抓取用戶配置文件JSON,並將其保存到本地存儲,以便您可以在任何需要的地方使用它。如果您無法掛鉤或訂閱其中之一,請在代碼中方便地執行此操作。找出你可以做什麼來獲取整個用戶JSON並保存如下...

請查看我下面的AuthService init()。第一行是this.authProvider.on('authenticated', this.onAuth);。無論您使用哪種身份驗證服務API,都應該爲您提供一種方法,讓您在有人登錄時指定回調(提供登錄令牌)。onAuth回調函數將令牌保存在本地存儲中,然後fetchProfile(...){...}再次調用身份驗證服務API以獲取使用令牌的整個JSON用戶配置文件剛剛收到this.user.getProfile(idToken, this.onProfile);。例如,我在項目中使用Auth0,並且我對Auth0 API的調用看起來像this.lock.getProfile(idToken, this.onProfile);,但我用您的調用可能看起來像this.user.getProfile(idToken, this.onProfile);的示例替換它。因此,請使用fetchProfile中的任何API使用替換。然後,onProfile回調將整個JSON配置文件保存在本地存儲中的一個密鑰中,使用this.localStorage.set('profile', profile);然後,您可以隨時撥打this.localStorage.get('profile')來獲取它。

  1. 不要通過延遲加載的ProfileComponent提供UserService。這會在您可能不想要的依賴注入樹上創建一個單獨的分支。請參閱https://angular-2-training-book.rangle.io/handout/modules/shared-modules-di.html將UserService導入到像AppModule或SharedModule這樣的頂級模塊中,並在那裏提供它。如果它位於AppModule中,則無需導出它。

app.module.ts

... 
@NgModule({ 
    imports: [ 
    ... 
    UserService, 
    ... 
    ] 
    providers: [ 
    ... 
    UserService, 
    ... 
    ] 
  • 手柄驗證相關的在驗證的東西,而不是檔案。配置文件看起來是可視的/特定於實現的(例如它有一個模板)。這是一個代碼片段的例子。
  • 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'); 
        } 
        } 
    
  • 互動與localStorage的通過LocalStorageService和訂閱變化如下。
  • localstorage.service.ts

    ​​
  • 不要做了這麼多的構造函數。例如:
  • 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 
    
        } 
    
    +0

    非常感謝您的幫助,丹。我仍然需要學習很多有關Angular 2的知識。我之前使用localStorage嘗試獲取經過身份驗證的用戶數據,但它看起來像只返回用於登錄(電子郵件,密碼)和用戶令牌的參數,相反從數據庫到整個用戶JSON。我應該能夠從本地存儲中獲取mongodb用戶json嗎? – universesurfer

    +0

    當然,很高興提供幫助。它有助於看到很多代碼示例!我將在上面的回答中添加一些評論,這可能會有所幫助。發表評論太長了:) – Dan

    +1

    非常感謝。它工作!唷。出於某種原因,我的JSON以{「key」:「value」}的文本格式返回,而不是可點擊的JSON格式。即使在解析之後。甚至先嚐試將其串化,然後解析。 – universesurfer

    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. 
    

    但你的導航欄鏈接不PASSI納克的id值

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

    需要提供細節做這樣的事情在你的導航欄

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

    謝謝Eduardo。我試過這樣做,但問題似乎是用戶信息不能從導航組件中包含在routerLink url中。我嘗試將導航欄作爲配置文件的子組件,並將用戶數據傳入,但它仍爲空。 – universesurfer

    +0

    所以現在你已經指出了問題的所在,你可以刪除所有的配置文件代碼,路由器代碼,併爲navbar提供component.ts? –

    +0

    我剛剛編輯以包含導航組件。儘管如此,我仍然保留其他代碼,因爲我不確定我使用的方法是否是最好的方法。 – universesurfer

    相關問題