0

I would like to parse a json file to use, and extract data.

I don't know why the data extracted from my code work only for my html, but is empty for my typescript code...

json file to parse :

[
  {
    "appleWatch": "generation_3",
    "bracelets": ["model_1","model_2","model_3"]
  },
  {
    "appleWatch": "generation_4",
    "bracelets": ["model_1","model_4","model_5"]
  }
]

Typescript of my component:

export class AppleKitComponent implements OnInit {
  constructor(private httpService: HttpClient) {}

  arrAppleWatch: AppleWatchModel[] = [];
  selectedWatch: AppleWatchModel = null;
  url = '../../assets/json/appleKit.json';

  ngOnInit() {
    this.arrAppleWatch = this.parseAppleWatchData();
    console.log(this.arrAppleWatch.toString() + 'test');
  }

  parseAppleWatchData() {
    this.httpService.get('../../assets/json/appleKit.json').subscribe(
      data => {
        this.arrAppleWatch = data as AppleWatchModel[]; // FILL THE ARRAY WITH DATA.
      },
      (err: HttpErrorResponse) => {
        console.log(err.message);
      }
    );
    return this.arrAppleWatch;
  }
}

My appleWatch model :

export class AppleWatchModel {
  constructor(
    public watch: string,
    public bracelets?: string[],
    public bracelet?: string
  ) {
  }
}

HTML:

{{arrAppleWatch |json }}

My log should output :

[ { "appleWatch": "generation_3", "bracelets": [ "model_1", "model_2", "model_3" ] }, { "appleWatch": "generation_4", "bracelets": [ "model_1", "model_4", "model_5" ] } ]

but it just prints an empty string.

My html work and show the array :

[ { "appleWatch": "generation_3", "bracelets": [ "model_1", "model_2", "model_3" ] }, { "appleWatch": "generation_4", "bracelets": [ "model_1", "model_4", "model_5" ] } ]
SiddAjmera
  • 38,129
  • 5
  • 72
  • 110
  • 3
    It's because `ngOnInit` has already been called when your data arrives, because the call is asynchronous, try to move the `log` to the `httpService` callback to confirm (right after the `this.arrAppleWatch = data as AppleWatchModel [];` line) – Kaddath Jul 15 '19 at 12:56
  • Why don't you type the http request? this.httpService.get('../../assets/json/appleKit.json'). Did you try https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify – MoxxiManagarm Jul 15 '19 at 12:59
  • 1
    Possible duplicate of [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – Heretic Monkey Jul 15 '19 at 13:10

2 Answers2

2

Your output is empty because you don't take the asynchronous nature of http requests into account. parseAppleWatchData is returned with the original arrAppleWatch value (which is []) before the http response is received. If you add some logs you will see B comes before A. You can also remove the return value.

export class AppleKitComponent implements OnInit {


  constructor(private httpService: HttpClient) {
  }

  arrAppleWatch: AppleWatchModel [] = [];
  selectedWatch: AppleWatchModel = null;
  url = '../../assets/json/appleKit.json';

  ngOnInit() {
    this.parseAppleWatchData();
    log('B', this.arrAppleWatch);
  }

 parseAppleWatchData() {
    this.httpService.get('../../assets/json/appleKit.json').subscribe(
      data => {
        this.arrAppleWatch = data as AppleWatchModel [];     // FILL THE ARRAY WITH DATA.
        console.log('A', data);
      },
      (err: HttpErrorResponse) => {
        console.log(err.message);
      }
    );
  }
MoxxiManagarm
  • 8,735
  • 3
  • 14
  • 43
2

There are a few issues with your implementation.

The httpService.get call call would be an async call. So it won't give you the data instantly. But you're trying to access it instantly. Hence you're not getting it in the Component Class.

Give this a try:

import { Component } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';

export interface AppleWatchModel {
  watch: string;
  bracelets?: string[];
  bracelet?: string;
};

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private httpService: HttpClient) {
  }

  arrAppleWatch: AppleWatchModel[] = [];
  selectedWatch: AppleWatchModel = null;

  ngOnInit() {
    this.parseAppleWatchData()
      .subscribe(res => {
        this.arrAppleWatch = res;
        console.log('test: ', this.arrAppleWatch);
      });
  }

  parseAppleWatchData() {
    return this.httpService.get<AppleWatchModel[]>('/assets/appleKit.json');
  }
}

Here, we're returning an Observable<AppleWatchModel[]> from parseAppleWatchData. So we can subscribe to it in the ngOnInit to get the actual data.


Here's a Working Sample StackBlitz for your ref.

SiddAjmera
  • 38,129
  • 5
  • 72
  • 110
  • Check [Async Pipe](https://angular.io/api/common/AsyncPipe) to avoid manual subscription. – youri Jul 15 '19 at 13:13
  • @youri, I'm aware of it. It's just that OP would definitely need the select watch at some point in time. Hence the manual subscription. Also, it's not like it's a never-ending Observable. So there's essentially no problem of a memory leak. So doesn't really matter for this case. – SiddAjmera Jul 15 '19 at 13:17