Jack Slingerland Jack Slingerland - 19 days ago 6
TypeScript Question

Angular 2 Service Singleton Scoping Problems

I have an Angular 2 service that I can't seem to get working correctly. For some reason the scoping on this isn't what I would expect it to be. I'm using fat arrows, which should keep things scoped correctly but I'm not sure where the wheels are falling off.

The Service

declare const Trello: any;

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

@Injectable()
export class TrelloService {
key: string = 'test';
token: string = 'test';
boards: any = [];

constructor(private http: Http) {
console.log('Initializing TrelloService. You should only see me once.');
}

getOpenBoards(): Promise<void> {
// 'this' is null here. No scope at all???
const url = `https://api.trello.com/1/member/me/boards?key=${this.key}&token=${this.token}`;
return this.http.get(url)
.toPromise()
.then((response: Response) => {
debugger;
this.boards = response.json();
});
}
}


The component below calls
getOpenBoards
on the service. When it does,
this
is null. This screams "crazy javascript scoping problem", but I have no idea what to do with it. Do I need to
bind
everywhere? If so, how would I even do that from the component?

The Component

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { CatchflyService } from '../../services/catchfly.service';
import { TrelloService } from '../../services/trello.service';

declare var Trello: any;

@Component({
selector: 'step-fetch-data',
templateUrl: './fetchData.component.html',
styleUrls: ['./fetchData.component.scss']
})
export class FetchDataComponent {
showError: boolean = false;

constructor(private trelloService: TrelloService,
private catchflyService: CatchflyService,
private router: Router) {

this.catchflyService.getProjects()
.then(this.trelloService.getOpenBoards)
.then(this.trelloService.getAllLists)
.then(() => {
console.log('finished getting projects, boards, and lists');
})
.catch((err) => {
console.log(err);
console.log('service rejected');
});
}
}

Answer

When you're calling the then function like you do:

this.catchflyService.getProjects()
  .then(this.trelloService.getOpenBoards)
  .then(this.trelloService.getAllLists)
  .then(() => {
    console.log('finished getting projects, boards, and lists');
  })
  .catch((err) => {
    console.log(err);
    console.log('service rejected');
});

You are passing the getOpenBoards function as a reference, which makes it lose it's binding to the object it's on. You can do one of two things:

1: Call the function directly in a handler:

this.catchflyService.getProjects()
  .then(i => this.trelloService.getOpenBoards())
  .then(i => this.trelloService.getAllLists())
  .then(() => {
    console.log('finished getting projects, boards, and lists');
  })
  .catch((err) => {
    console.log(err);
    console.log('service rejected');
});

2: Bind the function when passing it in:

this.catchflyService.getProjects()
  .then(this.trelloService.getOpenBoards.bind(this.trelloService))
  .then(this.trelloService.getAllLists.bind(this.trelloService))
  .then(() => {
    console.log('finished getting projects, boards, and lists');
  })
  .catch((err) => {
    console.log(err);
    console.log('service rejected');
});