0

I have a very simple injectable service in TypeScript for Angular 4. It fetches a JSON file as an array from the network. Afterwards, it should store the returned array in one of its properties, called data. data can then be accessed by other classes with the method getData().

My problem is that the assignment operation is ignored. Doing this.data = data does not actually change this.data. Everything else is working perfectly fine.

The code of the service:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class JsonFetcherService {
  private data: any;

  constructor(private http: HttpClient) {
    this.data = null;
  }

  public fetch(url: string): void {
    this.http.get(url).subscribe(this.setData);
  }

  private setData(data) {
    this.data = data;
  }

  public getData(): any {
    return this.data;
  }
}

The code of a very simple class that uses the service:

import { Component } from '@angular/core';

import { JsonFetcherService } from '../jsonFetcherService/JsonFetcherService';

@Component({
    selector: 'json-fetcher-example',
    templateUrl: './JsonFetcherExample.html'
})
export class JsonFetcherExample {
    constructor(private json: JsonFetcherService) {
        this.json.fetch('http://mysafeinfo.com/api/data?list=englishmonarchs&format=json');
    }
}

Update:

In addition to the issue of the binding of this, the method used here, subscribe, is asynchronous. The code to be executed after the JSON file is fetched needs to be moved inside the callback.

public fetch(url: string, myFunction) {
  const test = this.http.get(url);
  test.subscribe((data)=> {
    this.data = data;
    myFunction();
  });
}
Sᴀᴍ Onᴇᴌᴀ
  • 8,218
  • 8
  • 36
  • 58
Mat
  • 833
  • 1
  • 5
  • 20
  • `.subscribe(this.setData);` should be `.subscribe((data)=>this.setData(data));` or `.subscribe(this.setData.bind(this));`. You will lose the global `this` inside the callback if you omit the paranthesis `()` while writing the callback. – eko Sep 22 '17 at 04:47
  • @echonax The main cause of the problem is actually a combination of the context of `this` and of an asynchronous call, the duplicate you marked only partially answers the problem. – Mat Sep 22 '17 at 09:55
  • 1
    So the second part becomes the duplicate of https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call :-) – eko Sep 22 '17 at 10:02

1 Answers1

2

This issue here is the context of 'this' for "this.data". Inside the this.setData() function 'this' is actually referring to the observable. try this to fix it:

let setData = (data)=> {
  this.data = data;
}

Using Arrow notation allows the 'this' context to remain in the class rather than the observable.

More info on Arrow Notation

rjustin
  • 1,399
  • 8
  • 19
  • Thanks. Not sure what you meant. I was using the arrow notation when I first got the problem. I did: `this.http.get(url).subscribe((data)=> { this.data = data; });`. I'm not sure where I should put the `let`. – Mat Sep 21 '17 at 22:52
  • `this.http.get(url).subscribe((data)=> { this.data = data; });` should work, `this.http.get(url).subscribe(this.setData);` should not. – Harry Ninh Sep 21 '17 at 23:48
  • @Louis-MarieMatthews Either do what Harry suggested or change the setData function definition to a variable as in my answer. Every function has a 'this' unless you use arrow notation. – rjustin Sep 21 '17 at 23:52
  • Maybe you are expecting something like this `this.http.get(url).subscribe((data) => this.setData(data));` using the arrow notation – Sergio Contreras Sustaita Sep 22 '17 at 03:47
  • Acutally, the problem was twofold: I wasn't using array notation and the method is asynchronous. This meant `console.log` was called before `this.data = data`. I was originally using arrow notation, but because I didn't know the method was asynchronous, I tried to use a standard method instead, and this is the method I posted in my code snippet above. – Mat Sep 22 '17 at 09:54