Search code examples
javascriptangularpromisegoogle-api-js-clientgoogle-client-login

Trying to understand scope on angular 5 .then


On this example, the promise that i created works ok.

But the promise from the google api don't work.

It says that the this.youtube is undefined

index.html
<script src="https://apis.google.com/js/api.js"></script>

app.component.html

<button (click)="customClick()">custom Promise</button>
<hr>

<hello name="{{ youtube }}"></hello>
<button (click)="youtubeClick()">youtube Promise</button>

app.component.ts

import { Component } from '@angular/core';
import {  } from '@types/gapi.client.youtube';
import {  } from '@types/gapi.auth2';


export class AppComponent  {

  name = 'Angular 5';
  youtube = 'youtube';

  egPromise(){
    return new Promise<void>((resolve, reject) => {
      setTimeout(function(){
        resolve();
      }, 1000);
    });
  }

  customPromise(){
    this.egPromise().then( () => {
      this.name = 'from .then angular 5'
    });
  }

  customClick(){
    this.customPromise();
  }
/****************************************************/

youtubePromise(){
  gapi.client.init({
        apiKey: 'key',
        clientId: 'id',
        scope: "https://www.googleapis.com/auth/youtube.readonly",
        discoveryDocs: [
            "https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest"
        ]
        }).then(() => {
      this.youtube = 'from .then youtube'
    });
}

youtubeClick(){
  gapi.load('client:auth2', this.youtubePromise);
}

Edit: Solution/Explanation

With the help of @vivek-doshi

I found this post searching "bind this"

https://www.sitepoint.com/bind-javascripts-this-keyword-react/

And as the post explain

"it’s not always clear what this is going to refer to in your code, especially when dealing with callback functions, whose callsites you have no control over."

Since I'm working with the google API and i have no control over that code.

"This is because when the callback to the promise is called, the internal context of the function is changed and this references the wrong object."

And the function to load the library use a callback function, and don't even crossed my mind that the first callback was the problem.

So using the ES2015 Fat Arrows function, as the post says.

"they always use the value of this from the enclosing scope." ... "Regardless of how many levels of nesting you use, arrow functions will always have the correct context."

So instead of creating binding and self and that and wherever, I think that is more clean the use of =>

Other thing that it was confunsing me is that the google api aks for a callback with no argument.

So if you try to use const that = this; gapi.load('client:auth2', this.start(that) );

It will complain.

But using gapi.load('client:auth2', () => this.start() ); I'm not passing an argument.

This things could be simple for a lot of people, but since I'm learning, I will try to make it simple for others that are learning too.

Thanks everyone.


Solution

  • Here you are losing the scope of this by calling :

    gapi.load('client:auth2', this.youtubePromise);
    

    Change above code to :

    gapi.load('client:auth2', () => this.youtubePromise()); // ES6 Way
    // OR
    gapi.load('client:auth2', this.youtubePromise.bind(this)); // Traditional old way of doing
    

    WORKING DEMO