Before I start my question, I'd like to let you know that I already did a big research, and I cannot find a solution ( explanation ) why I get this error.
Please also note, that I am totally new on Angular, and I just started to learn how it works.
So, the problem I have is what I have entered in the title of this question.
What I try to do, is to build a login system using the Firebase, based on a course I purchase on Udemy.
The code I use is the following:
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 ();
    }
}
data-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
    );
  }
}
So, unfortunately, I cannot see what could be wrong with this code. Can anyone spot anything I did wrong?
Note: I have removed parts of my code just for make the snippets more readable. If you need any other part, please feel free to ask me, and I will provide it here.
UPDATE #1
This is how it looks like the 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>
                I got the same error from using an unitialized EventEmitter:
@Output() change: EventEmitter<any>;
instead of:
@Output() change: EventEmitter<any> = new EventEmitter<any>();
The error occurred in the higher-level component that tried to subscribe to the change event.
The issue seems to be the order in which your code gets executed, more specifically the getRecipes() method :
// 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;
}
The solution to this is that your getRecipes () method SHOULD NOT SUBSCRIBE. It should return either a Promise or an Observable.
Something like this:
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());
}
Then, the calling code in HeaderComponent becomes :
const recipeObs = this.dataStorage.getRecipes();
recipesObs.subcribe(jsonData => {
  // Use the JSON data from the HTTP response
});
Several remarks:
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
getRecipes(). ALWAYS subscribe at the last minute possible. You can subscribe multiple times to the same observable, but be aware that each subscription re-executes the observable (in the case of an http request, it means you run the request multiple times; not ideal...).recipeSubscription since it contains an Observable, not a Subscription. A subscription is what subscribe() returns. In other words: const subscription = observable.subscribe().Problem
I stumble upon the same error and the reason was that I was initializing my @Output event emitter inside ngOnInit().
export class MyClass implements OnInit {
    @Output()
    onChange : EventEmitter<void>;
    ngOnInit() {
        // DO NOT initialize @Output event here
        this.onChange = new EventEmitter<void>();    
    }
}
Solution
When I changed the initialization to the same place of the declaration it worked.
export class MyClass implements OnInit {
    @Output()
    onChange : EventEmitter<void> = new EventEmitter<void>();
    ngOnInit() {
    }
}
I think this happens because the parent component tries to subscribe to the event too soon (before ngOnInit() is triggered).
The problem is, you're returning an observable and re-assigning it in the response of Token().
Try making a Subject of the Observable you have now, i find these easier to use.
public recipeSubscription: Subject<any> = new Subject();
Change your assignment from
this.recipeSubscription = this.http.get....
To
let response = this.http.get....
Subscribe on that within the function this gets called:
response.subscribe((res) => {this.recipeSubscription.next(res)})
Now you can subscribe directly on the property
this.dataStorage.recipeSubscription.subscribe((res) => {
    // Do stuff.
});
this.dataStorage.getRecipes();
I hope this is enough to help you :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With