-1

I am trying to convert the JSON of an interface given by an API to different arrays which consist of dedicated objects. The type of the object is given as an variable in the interface.

The interface:

export interface Interface{
  interfaceClassType: string;
}

I am accessing my json data like this:

getJSON(): Observable<Interface[]> {
    return this.http.get<Interface[]>(URL)
      .pipe(
        retry(2),
        catchError(this.handleError)
      );
  }

This method is called like this:

 arrayWithObjects: Interface[];
 class1Array: Class1[];
 class2Array: Class2[];

 processJSON(): void {
    this.configService.getJSON().subscribe(results => this.arrayWithObjects = results);
    this.arrayWithObjects.forEach (function (object) {
      switch (object.interfaceClassType) {
        case "first":
          this.class1Array.push(object as Class1);
          break;
        case "second":
          this.class2Array.push(object as Class2);
          break;
      }
    }.bind(this))
  }

I get the same result when i call it like this:

 arrayWithObjects: Interface[];
 class1Array: Class1[];
 class2Array: Class2[];

 processJSON(): void {
    this.configService.getJSON().subscribe(results => this.arrayWithObjects = results);
    this.arrayWithObjects.forEach ((object) => {
      switch (object.interfaceClassType) {
        case "first":
          this.class1Array.push(object as Class1);
          break;
        case "second":
          this.class2Array.push(object as Class2);
          break;
      }
    })
  }

When I execute this methods i always get an error saying: ERROR TypeError: this.class1Array is undefined

bena
  • 45
  • 1
  • 5
  • the error is very clear. Where is defined your class1Array object? – AlleXyS Jul 23 '20 at 13:03
  • 1
    use arrow function, not `function`: `forEach ((object) => { })`. There are so many examples of this question out there. But hard to track them down – Poul Kruijt Jul 23 '20 at 13:08
  • I already looked at many similar questions and i already tried this way, but somehow this also doesn't work for me – bena Jul 23 '20 at 13:14
  • @bena: You have 2 issues. You're trying to access the async variable `this.arrayWithObjects` synchronously and using `this` keyword in a JS `function` which will point to the scope of the function, not the class. – ruth Jul 23 '20 at 13:18
  • @MichaelD As a gold badge holder, you can close the question as a duplicate of both questions... – Heretic Monkey Jul 23 '20 at 13:20
  • In the code you posted you don't ever give `class1Array` a value. The line `class1Array: Class1[];` is just declaring the variable, but not populating it. If you change it to `class1Array: Class1[] = [];` then you're also populating it (with an empty array). You can then push data into the array. – Tim Jul 23 '20 at 13:25
  • @Tim This actually solved my problem. – bena Jul 23 '20 at 14:09
  • Good stuff :) I don't know why this question was closed as a duplicate, I have voted to reopen it. Perhaps people didn't read the error message you provided and just looked for problems in the code. Anyway, glad to have helped :) – Tim Jul 23 '20 at 14:17

2 Answers2

0

Using a normal function as callback won't preserve the scope of this inside it. Using an arrow function will solve that.

this.arrayWithObjects.forEach ((object) => {
      console.log(this.class1Array);
})
MonkeyScript
  • 4,776
  • 1
  • 11
  • 28
  • Why? What good does this do? Answers which explain a change made to code are better than those that simply regurgitate advice. – Heretic Monkey Jul 23 '20 at 13:14
  • @HereticMonkey Using normal functions as callback wont preserve `this` scope. – MonkeyScript Jul 23 '20 at 13:15
  • I know. [Edit] your answer to provide the information to the OP and others who come across the answer. Stack Overflow is about providing lasting answers that help everyone who reaches the question and its answers, not just the OP. See [answer]. – Heretic Monkey Jul 23 '20 at 13:18
0

There are 2 issues here.

  1. this.arrayWithObjects is assigned asynchronously. By the time you do the forEach it still is unassigned. To solve it, move the forEach inside the subscription.
processJSON(): void {
  this.configService.getJSON().subscribe(results => {
    this.arrayWithObjects = results;
    this.arrayWithObjects.forEach (
      ...
    );
}

More about async data here.

  1. Use arrow function notation instead of conventional JS function to point the this keyword to the class member variables. In a JS function, this points to the scope of the function, not the class.
processJSON(): void {
  this.configService.getJSON().subscribe(results => {
    this.arrayWithObjects = results;
    this.arrayWithObjects.forEach((object) => {       // <-- arrow function here
      switch (object.interfaceClassType) {
        case "first":
          this.class1Array.push(object as Class1);
          break;
        case "second":
          this.class2Array.push(object as Class2);
          break;
      }
    });               // <-- `bind()` not required
  });
}
ruth
  • 29,535
  • 4
  • 30
  • 57