1

I am learning Angular2, following the "Tour of Heroes" tutorial on Angular.io. Near the end of the tutorial, we set up routing to a detail page and pass a parameter indicating the hero to displace. This is handled using the params Observable in ActivatedRoute. We use switchMap to redirect from the params Observable to a Promise to return the data we actually want based on the parameter.

The syntax used in the tutorial is concise, so I tried to break it out into building blocks to get a better understanding of what is going on. Specifically, I have tried to replace right arrow notation with an actual function, that I think is identical. But my modification does not work.

Here is the code:

ngOnInit(): void {
        this.route.params
        .switchMap((params: Params) => this.heroService.getHero(+params['id']))
        //.switchMap(this.getHero)
        .subscribe(hero => this.hero = hero);
    }

    getHero(params: Params) : Promise<Hero> {
        return this.heroService.getHero(+params['id']);
    }

What confuses me is why using the line that is currently commented out instead of the line above it, I get an error: "Cannot read property 'getHero' of undefined." The two versions of code look identical to me.

Roman C
  • 49,761
  • 33
  • 66
  • 176
Raj Singh
  • 15
  • 1
  • 5
  • 1
    One would be a method, the other an arrow function with bound `this`. Are you sure it's erroring on the `.getHero`, not the `.heroService` inside the function? – Bergi Aug 10 '17 at 04:05
  • Possible duplicate of [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – eko Aug 10 '17 at 04:31

3 Answers3

1

Fat-arrow function preserves the context of execution, allowing the this "variable" to be the same as in the parent scope. If you use .switchMap(this.getHero) then this will point to something else, not the component.

getHero(params: Params) : Promise<Hero> {
    // "this" is not what you expect here
    return this.heroService.getHero(+params['id']);
}

So this.heroService is undefined here.

admax
  • 1,671
  • 2
  • 16
  • 23
1

You'd need to bind your getHero function.

.switchMap(this.getHero.bind(this))

Otherwise your change is identical. Using bind like this allows you to pass getHero as a standalone function to switchMap without losing what this means to it.

You can experiment with it:

'use strict';
const foo = { 
  bar: 'baz',
  getBar: function() {
    return this.bar;
  }
};

foo.getBar();
// => 'baz'

const myGetBar = foo.getBar;
myGetBar();
// => TypeError: Cannot read property 'bar' of undefined

const boundGetBar = foo.getBar.bind(foo);
boundGetBar();
// => 'baz'

const otherObj = { bar: 'hi' };
const otherBoundGetBar = foo.getBar.bind(otherObj);
otherboundGetBar();
// => 'hi'

otherObj.getBar = myGetBar;
otherObj.getBar();
// => 'hi'
Chad
  • 2,161
  • 1
  • 19
  • 18
0

You cannot use this.getHero like in your snippet because

  1. it's undefined (the service returns Observable that you have to subscribe before using its data)
  2. it's not a property (doesn't have get modifyer).
Roman C
  • 49,761
  • 33
  • 66
  • 176