2017-10-24 16 views
3

質問を開始する前に、私は既に大きな研究をしたことをお知らせしたいと思います。 。角度 "プロパティを読み取ることができません"未定義の "

私はAngularについて全く新しいことに注意してください。そして、それがどのように機能するかを学び始めました。

私はこの問題のタイトルに入力した問題があります。

私がしようとしているのは、Udemyで購入したコースに基づいて、Firebaseを使用してログインシステムを構築することです。

I、使用コードは以下の通りである:

auth.service.ts

import {Injectable} from '@angular/core'; 
import * as firebase from 'firebase'; 

@Injectable() 
export class AuthService { 
    token: string; 

    // ... 

    singInUser (email: string, password: string) { 
     // login process here ... 
    } 

    // Responsible to retrieve the authenticated user token 
    getToken() { 
     return firebase 
      .auth() 
      .currentUser 
      .getIdToken(); 
    } 
} 

データstorage.service.ts

// ... Dependencies here 
@Injectable() 
export class DataStorageService { 
    private recipeEndPoint: string = 'https://my-unique-id.firebaseio.com/recipes.json'; 
    private recipeSubscription: Observable<any> = new Observable(); 

    constructor (private http: Http, 
        private recipes: RecipeService, 
        private authService: AuthService) {} 

    // other functionality ... 

    getRecipes() { 
     const token = this.authService.getToken(); 

     token.then (
      (token: string) => { 
       this.recipeSubscription = this.http.get (this.recipeEndPoint + '?auth=' + token).map (
        (data: Response) => { 
         return data.json(); 
        } 
       ); 

       // THIS PARTICULAR CODE WORKS AS EXPECTED 
       // WITH NO ISSUES 
       this.recipeSubscription.subscribe (
        (data: Response) => { 
         console.log ('Data response: ', data); 
        }, 
        (error) => { 
         console.log ('Error: ' + error); 
        } 
       ) 
      } 
     ); 

     // This is supposed to return an Observable to the caller 
     return this.recipeSubscription; 
    } 
} 

header.component .ts

// Dependencies here ... 

@Component({ 
    selector: 'app-header', 
    templateUrl: './header.component.html', 
    styleUrls: ['./header.component.css'] 
}) 
export class HeaderComponent implements OnInit { 
    constructor(private dataStorage: DataStorageService, private recipeService: RecipeService) { } 

    // Other Code Here ... 

    onFetchData() { 
    let recipeSubscription = this.dataStorage.getRecipes(); 

    // THIS RETURNS TRUE 
    console.log(recipeSubscription instanceof Observable); 

    // THIS LINE THEN RETURNS THE MESSAGE: 
    // ERROR TypeError: Cannot read property 'subscribe' of undefined 
    recipeSubscription.subscribe(); 

    // IF I COMMENT OUT THE PREVIOUS LINE 
    setTimeout(
    () => { 
     // THIS RETURNS TRUE 
     console.log(recipeSubscription instanceof Observable); 
     }, 
     500 
    ); 

    setTimeout(
    () => { 
     // AS WELL THIS ONE RETURNS TRUE 
     console.log(recipeSubscription instanceof Observable); 
     }, 
     1000 
    ); 

    setTimeout(
    () => { 
     // AS WELL THIS ONE RETURNS TRUE 
     console.log(recipeSubscription instanceof Observable); 
     }, 
     1500 
    ); 
    } 
} 

残念ながら、このコードで何が間違っているかわかりません。私が間違っていたものは誰でも見つけられますか?

注:スニペットを読みやすくするためにコードの一部を削除しました。他の部分が必要な場合は、私に相談してください。私はここでそれを提供します。

UPDATE#1

これは、問題はより具体的にあなたのコードが実行されますするため、getRecipes()方法であるように思わheader.component.html

<nav class="navbar navbar-default"> 
    <div class="container-fluid"> 
     <div class="navbar-header">Logo Here</div> 

     <div class="navbar-default"> 
      <ul class="nav navbar-nav"> 
       <!-- Left Navigation Options --> 
      </ul> 
      <ul class="nav navbar-nav navbar-right"> 
       <!-- Right Navigation Options --> 
       <li class="dropdown" appDropdown> 
        <a routerLink="/" class="dropdown-toggle" role="button">Manage <span class="caret"></span></a> 
        <ul class="dropdown-menu"> 
         <li> 
          <a style="cursor: pointer;" (click)="onSaveData()">Save Data</a> 
         </li> 
         <li> 
          <!-- Here is where I call the onFetchData method --> 
          <a style="cursor: pointer;" (click)="onFetchData()">Fetch Data</a> 
         </li> 
        </ul> 
       </li> 
      </ul> 
     </div> 
    </div> 
</nav> 
+0

あなたは 'header.component.ts'で' onFetchData() 'メソッドを呼び出しますどこから? – AlexWoe89

+0

データストレージサービスでは、すでに購読しています。 –

+0

コード '(click)=" onFetchData() "を使ってコンポーネントHTMLから' onFetchData() 'が呼び出されました –

答えて

5

どのように見えるかです:

// Numbers indicate the execution order 

getRecipes() { 
    const token = this.authService.getToken(); 

    // 1. You call a promise, which will take a while to execute... 
    token.then (
     (token: string) => { 
      // 3. Finally, this bit gets executed, but only when the promise resolves. 
      this.recipeSubscription = ... 
     } 
    ); 

    // 2. Then, you return a variable that hasn't been assigned yet, 
    // due to the async nature of the promise. 
    return this.recipeSubscription; 
} 

これに対する解決方法は、getRecipes()の方法はサブスクライブしないことです。 は、プロミスまたはオブザーバブルのいずれかを返す必要があります。このような

何か:

getRecipes() { 
    // Convert the initial promise into an observable 
    // so can you use operators like map(), mergeMap()... to transform it. 
    const tokenObs = Observable.fromPromise(this.authService.getToken()); 

    // Merge the token observable into an HTTP observable 
    // and return the JSON data from the response. 
    return tokenObs 
     .mergeMap(token => this.http.get('XXX?auth=' + token)) 
     .map(resp => resp.json()); 
} 

その後、HeaderComponentで呼び出し元のコードは次のようになります。

const recipeObs = this.dataStorage.getRecipes(); 
recipesObs.subcribe(jsonData => { 
    // Use the JSON data from the HTTP response 
}); 

いくつかの発言:あなたが明示的に使用されるRxJS演算子をインポートする必要が

  • あなたのコード。あなたは私の例に従えば、あなたは最初に、次のインポートを追加する必要があります。
import 'rxjs/add/observable/fromPromise'; 
import 'rxjs/add/operator/map'; 
import 'rxjs/add/operator/mergeMap'; 
  • あなたは、観察を作成する方法で購読することはありません。あなたの場合、getRecipes()に登録しないでください。できるだけ早く購読してください。同じ観察可能なものに複数回サブスクライブすることもできますが、各サブスクリプションがオブザーバブルを再実行することに注意してください(http要求の場合は、複数回実行することを意味します。
  • 変数recipeSubscriptionにはObservableが含まれているため、ではなく、変数を呼び出すことはお勧めできません。サブスクリプションはsubscribe()が返すものです。言い換えれば:const subscription = observable.subscribe()
  • Firebase SDKを直接使用しています。あなたはAngularFire libraryを知っていますか?
+0

私が理解しているかどうかは分かりません。ファイヤーベースのライブラリチェックで返されたトークンを取得した後、Firebaseにレシピを読み込むためにリクエストを送信したいと思います。最後にレシピを取得したら、 (getRecipes()のログインが複雑すぎることがわかりますが、私はそうする方法の手掛かりはありません:( –

+0

ところで@AngularFrance、私はこれまでのところあなたの助けを感謝します:) –

+0

説明するために完全なコードサンプルで私の答えを更新しました。 – AngularChef

3

問題は、観測対象を返して、Token()の応答でそれを再割り当てすることです。

ObservableのSubjectを作成してみてください。私はこれを使いやすくしています。

response.subscribe((res) => {this.recipeSubscription.next(res)}) 

今、あなたは上で直接登録することができます:

public recipeSubscription: Subject<any> = new Subject(); 
は、これは呼び出される関数内でその上で購読

let response = this.http.get.... 

this.recipeSubscription = this.http.get.... 

からあなたの割り当てを変更し

プロパティ

this.dataStorage.recipeSubscription.subscribe((res) => { 
    // Do stuff. 
}); 

this.dataStorage.getRecipes(); 

私は、これはあなたを助けるために十分であると思います:)

関連する問題