Angular "Cannot read property 'subscribe' of undefined"

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 :)


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.


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 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:

  • You need to explicitly import the RxJS operators used in your code. If you follow my example, you need to add the following imports at the beginning:
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
  • You should NEVER subscribe in the method that creates the observable. In your case, don't subscribe in 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...).
  • It is not a good idea to call your variable recipeSubscription since it contains an Observable, not a Subscription. A subscription is what subscribe() returns. In other words: const subscription = observable.subscribe().
  • I see that you're using the Firebase SDK directly. Are you aware of AngularFire library?