Search code examples
jsonangulartypescriptobservablehttp-get

I'm Trying to get key value pairs from json data via an external api and display it using angular and typescript. How can I achieve this?


I would like to get each key value pair in the results object from my api to display on my front-end. ie(category, type, difficulty, questions correct_answer) I have the service and components set up correctly and all i need to do is fetch the json and display each pair. The method name is called fetchQuestions as seen below. I was able to successfully get data by just simply calling it the same way I did fetchPeople but the json format is not the same so it's not displaying. I then tried something else but that's not working either. How can I acheive displaying this?

People.service

 import { Injectable } from '@angular/core';
 import { Observable } from  'rxjs/Observable';
 import { HttpClient } from '@angular/common/http';
 import { map } from 'rxjs/operators';

 @Injectable({
 providedIn: 'root'
 })
 export class PeopleService {

 constructor(private http: HttpClient) { }


//this works like a charm as it is just a simple array

fetchPeople(): Observable <Object> {
return this.http.get('/assets/data/people.json')
}




// this doesn't work as the json from the api below is in a different 
  // json format. Plus I need to return all value pairs

fetchQuestions(): Observable <Object> {
return this.http.get('https://opentdb.com/api.php? 
 amount=10&difficulty=hard&type=boolean').map(res => 
res.json().results).subscribe(data => {
console.log(data););
    }
  }

API https://opentdb.com/api.php?amount=10&difficulty=hard&type=boolean

   {
   "response_code": 0,
   "results": [
   {
   "category": "Vehicles",
   "type": "boolean",
   "difficulty": "hard",
   "question": "In 1993 Swedish car manufacturer Saab experimented 
   with replacing the steering wheel with a joystick in a Saab 9000.",
   "correct_answer": "True",
   "incorrect_answers": [
    "False"
   ]
   },
   {
  "category": "History",
  "type": "boolean",
  "difficulty": "hard",
  "question": "Japan was part of the Allied Powers during World War 
  I.",
  "correct_answer": "True",
  "incorrect_answers": [
    "False"
  ]
  },
  {
  "category": "History",
  "type": "boolean",
  "difficulty": "hard",
  "question": "The Kingdom of Prussia briefly held land in Estonia.",
  "correct_answer": "False",
  "incorrect_answers": [
    "True"
  ]
  },
  {
  "category": "Science: Mathematics",
  "type": "boolean",
  "difficulty": "hard",
  "question": "The binary number &quot;101001101&quot; is equivalent 
  to the Decimal number &quot;334&quot;",
  "correct_answer": "False",
  "incorrect_answers": [
    "True"
  ]
  },
  {
  "category": "Entertainment: Video Games",
  "type": "boolean",
  "difficulty": "hard",
  "question": "TF2: Sentry rocket damage falloff is calculated based 
 on the distance between the sentry and the enemy, not the engineer 
  and the enemy",
  "correct_answer": "False",
  "incorrect_answers": [
    "True"
  ]
 },
  {
  "category": "Entertainment: Video Games",
  "type": "boolean",
  "difficulty": "hard",
  "question": "The names of Roxas&#039;s Keyblades in Kingdom Hearts 
  are &quot;Oathkeeper&quot; and &quot;Oblivion&quot;.",
  "correct_answer": "True",
  "incorrect_answers": [
    "False"
  ]
},
{
  "category": "Entertainment: Music",
  "type": "boolean",
  "difficulty": "hard",
  "question": "The band STRFKR was also briefly known as Pyramiddd.",
  "correct_answer": "True",
  "incorrect_answers": [
    "False"
  ]
},
{
  "category": "Entertainment: Books",
  "type": "boolean",
  "difficulty": "hard",
  "question": "Harry Potter was born on July 31st, 1980.",
  "correct_answer": "True",
  "incorrect_answers": [
    "False"
  ]
},
{
  "category": "Entertainment: Japanese Anime & Manga",
  "type": "boolean",
  "difficulty": "hard",
  "question": "Druid is a mage class in &quot;Log Horizon&quot;.",
  "correct_answer": "False",
  "incorrect_answers": [
    "True"
  ]
},
{
  "category": "Geography",
  "type": "boolean",
  "difficulty": "hard",
  "question": "The two largest ethnic groups of Belgium are Flemish and Walloon. ",
  "correct_answer": "True",
  "incorrect_answers": [
    "False"
     ]
   }
 ]
}

People.page.ts

 import { Component, OnInit } from '@angular/core';
 import { PeopleService } from './../../providers/people.service'
 @Component({
 selector: 'app-people',
 templateUrl: './people.page.html',
 styleUrls: ['./people.page.scss'],
 })
 export class PeoplePage implements OnInit {

 people$;
 results$;
 constructor(private peopleService:PeopleService) { }

 fetchPeople(){
 this.people$ = this.peopleService.fetchPeople();
 }
 fetchQuestions(){
  this.results$ = this.peopleService.fetchQuestions()
 }




  ngOnInit() {
  }

 }

People.page.html

   <ion-toolbar>
   <ion-title>people</ion-title>
   </ion-toolbar>
   </ion-header>
   <ion-button (click) ="fetchPeople()"  color="primary">Primary</ion- 
   button>

   <ion-list>
   <ion-item *ngFor="let person of people$ | async">{{person.name}}. 
   </ion-item>
   </ion-list>
   <ion-button (click) ="fetchQuestions()"  
   color="primary">Secondary</ion-button>
   <ion-list>
            <ion-item *ngFor="let result of results$ | async">. 
             {{result.category}}</ion-item>
             <ion-item *ngFor="let result of results$ | async"> 
            {{result.questions}}</ion-item>
           <ion-item *ngFor="let result of results$ | async"> 
           {{result.difficulty}}</ion-item>
           <ion-item *ngFor="let result of results$ | async"> 
            {{result.correct_answer}}</ion-item>
   </ion-list>
  <ion-content>
 <ion-button color="primary">Primary</ion-button>
 </ion-content>

Solution

  • The HTTP GET is a cold observable. As such each async will fire an individual request. Besides you aren't actually returning the observable from the fetchQuestions() function. The subscription should be removed.

    You could also use Angular HttpParams to set the query parameters and do things the Angular way.

    Try the following

    Service

    import { HttpClient, HttpParams } from '@angular/common/http';
    
    fetchQuestions(): Observable<any> {
      const params = new HttpParams()
        .set('amount', '10')
        .set('difficulty', 'hard')
        .set('type', 'boolean');  
      return this.http.get('https://opentdb.com/api.php', { params: params })
        .pipe(map(res => res.results));
    }
    

    Template

    <ng-container *ngFor="let result of results$ | async">
      <ion-list>
        <ion-item>{{result.category}}</ion-item>
        <ion-item>{{result.question}}</ion-item>
        <ion-item>{{result.difficulty}}</ion-item>
        <ion-item>{{result.correct_answer}}</ion-item>
      </ion-list>
    </ng-container>
    

    I also noticed a small type. result.questions should actually be result.question.

    More details about hot vs cold observables: https://blog.thoughtram.io/angular/2016/06/16/cold-vs-hot-observables.html

    Working example: Stackblitz