1

I'm working on an Angular 2 application, and I'm trying to use JSON data, either local/mocked or fetched via HTTP, and display it on a component. I have an injectable service that will do the fetching/mocking -

import { Injectable } from 'angular2/core';

@Injectable()
export class TestService {
  testString:string = "";
  testDetails: string = "";

  constructor() { }

  getTestDetails(): Promise<string> {
    this.testDetails = {
      "status": "success",
      "message": "Data save successful",
      "data": {
        "Random_Data_1": "Random Data 1",
        "Random_Data_2": "Random Data 2"
      }
    };
    return Promise.resolve(JSON.stringify(this.propertyDetails));
  }
}

And then I have a component that uses the service via Dependency Injection -

import { Component, OnInit } from 'angular2/core';
import {TestService} from "./test.service";

@Component({
  selector: 'test',
  templateUrl: './test.component.html',
  styleUrls: []
})
export class TestComponent implements OnInit {
  testDetails: string = "";

  constructor(private testService: TestService) { }

  ngOnInit() {
    this.display();
  }

  display(): void {
    this.testService.getTestDetails()
      .then(
        testDetails => {
          this.testDetails = JSON.parse(testDetails);
        },
        errorMessage => {
          console.error("Something failed trying to get test details");
          console.error(errorMessage);
        }
      );
  }
}

The component HTML -

<div class="content">
  <p> Test Details </p>
  <p> {{ testDetails.data.Random_Data_1 }} </p>
</div>

The problem is, the HTML is erroring out trying to display the items in the testDetails JSON. I initially used it with md-tabs, so the first try would error out, but the other tabs would read the data fine. Also, the ngOnInit would be called twice when the error occurs. I have narrowed it down to the data coming in and the object types that is causing me the headache.

I know I can create a Details class and declare testDetails of type Details, and then map the JSON data into the class, but the thing is, I want to work with generic data, and only know a few components that will be present in the data. Is there a way to read the JSON, and use the data without having to define a separate class for each scenario ?

I have a plunker with the most basic stuff set up. The actual setup runs fine on my local system up until where I try to access the JSON data in the HTML, at which point the browser throws a cryptic error. The skeleton code doesn't even run on Plunker. That said, the structure in the Plunker defines the structure of my app and the data flow. Plunker with the basic setup

What is the best way to achieve this ? What is the standard/best practice to do this ?

3
  • template parser error ? are you getting? Commented Apr 6, 2017 at 2:52
  • 1
    testDetails?.data?.Random_Data_1. Maybe the data is not available yet on first rendering, so you should use the ? syntax or you will get null/undefined errors Commented Apr 6, 2017 at 4:07
  • Yes, the data not being available is my problem, but using the ? resolved the issue for me. I assumed the view would wait to render until the promise resolved, which in hindsight does not make sense. Thank you for the solution, I will accept it as a solution if you submit as an answer. Commented Apr 7, 2017 at 1:06

2 Answers 2

1

Throwing another option out there, since you asked about best way to achieve this. Might not be the best idea, this is subjective ;) But if I were you...

Thinking about the future, where you will use real backend, it could be nice to use mock json file. If/when you move over to a real backend, you wouldn't basically need to change anything else but the url of the requests :)

So I set up a simple example for you. Here I used Observables, but you can use Promises if you prefer that. Here's more info on HTTP if you want/need to read up on that. Most important thing is that you have the HttpModule imported in your app module.

You have your file with JSON and in your service make http-requests to that:

getTestDetails() {
  return this.http.get('src/data.json')
    .map(res => res.json())
}

Your display-method:

display() {
  this.testService.getTestDetails()
    .subscribe(data => {
      this.testDetails = data;
    });
}

And in the template use the safe navigation operator to safeguard null/undefined values:

<div class="content">
  <p> Test Details </p>
  <p> {{ testDetails?.data?.Random_Data_1 }} </p>
</div>

Here's a

Demo

As said, this is to give another approach on how to implement the things you want to achieve, and this would probably be my preferred way :)

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for the complete solution, along with the Plunker. I do have a partial mocking setup in place, but thank you for helping me complete it. My question about best practice was more along the lines of can I use the JSON in my views directly, or do I have to filter it through a class, but now I know I can directly use the JSON, using a ?, and the data will be rendered when available.
Ah, okay. Yes, well classes are not needed no and in my opinion shouldn't be used if you really do not need classes for some reason. If you want to type your data (which you do not want in this case, but IF), I'd use interfaces instead. But this wouldn't matter during runtime though, as there is no interfaces in JS. (TS is compiled to JS during runtime). Interfaces are more of a "help" for the developer and IDE which can warn for example when you are trying to assign wrong values or wrong types of values.
And furhermore, even if you would use classes, it wouldn't help, as this is still an asynchronous event and you'd need to use the safe navigation operator (or ngIf) to safeguard null and undefined values anyway.
1

Use

<p *ngIF="testDetails.data.Random_Data_1 "> {{ testDetails.data.Random_Data_1 }} </p>

This is because there is no data initially.Hope this helps you.

1 Comment

The problem definitely is with my object not having any data to begin with, but I think using *ngIf on all the data I want to display makes it a little cumbersome. The ? works just fine for my use case, which is what I decided to use.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.