HttpClient as provider


#1

Hi!

I have created a Provider used for retrieving data from a JSON file.

@Injectable()
export class JsonProvider {

  public plan:Array<any>=[];

  constructor(public http: HttpClient) {
    this.http.get('../assets/data/arbeidsplan.json').subscribe(data => {
    this.plan.push(data);
          });
  }
}

And in the typescript file of my homePage, I have the following constructor

constructor(public navCtrl: NavController, public locationTracker: LocationTracker, public http: HttpClient, public jsonProvider : JsonProvider) {
    this.jsonProv = jsonProvider;
    console.log(this.jsonProv.plan)
  }

The problem is that the page loads before the data is retrieved, and therefore “undefined” is written to the console, and I get the error “Can’t read … of undefined” when the data from the JSON file is used in another method.

How can I make sure that things are done in the correct order?


#2

This is the wrong question. Instead ask “how can I make it so that I don’t care what happens in what order?”.


#3

Thank you!

How can I do that, then? :slight_smile:


#4

Well, presumably your proximate problem is that you have some property that is being referenced from a template. If you always make sure to initialize all arrays and objects, even if to something as mundane as [] or {}, that should eliminate “cannot read property ‘foo’ of undefined” errors. Once the asynchronous task has completed, Angular’s change detection will handle the rest.

If you have a function B that needs the result of function A, then look into the mergeMap operator. The way I think about this is that there are two kinds of functions: producers and consumers. Producers should never be calling subscribe, and should generally have the first word in them be return. Consumers should be given exactly what they are looking for (which often makes more work in the providers, but it’s worth it).

So most every producer looks something like:

return this.http.get(url).pipe(
  map(foo => mogrify(foo)),
  mergeMap(bar => frotzify(bar)));

…every consumer looks like:

thingies = [];
this.provider.getThingies().subscribe(t => this.thingies = t);

…and that way you get no errors in templates such as:

<div *ngFor="let thingy of thingies"></div>

#5

Thank you! Had forgotten to initialize one of the arrays.