Search code examples
angularapiangular-httpclientsubscribe

Having trouble with Observe/Subscribe on API call to JokeAPI in my angular app


I have spent the better part of 3 days on this now and I just know it's going to be something stupid. I have my api call working, I am getting a response, but my ngFor loop is giving me a headache with this error:

Console Error

You can see that my API is returning a data packet at the bottom of the picture. I tested the component with standard divs that loaded fin in the home component, so I know its not an issue there. Near as I can tell, even though I am using the Observe and Subscribe, It looks like the ngFor loop is tring to run fore the data come back from the call. Here are my typescript files:

//joke.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Injectable({providedIn: 'root'})

export class JokesService {
  //service api
  apiURL = 'https://v2.jokeapi.dev/joke/';

  constructor(private http: HttpClient) {}

  //optional filters
  numJokes = 10;
  //add more than one category using comma separation
  categories = 'Programming'; //options: Programming,Misc,Dark,Pun,Spooky,Christmas or 'Any' for all categories
  //add more than one flag using comma separation
  blacklistFlags = 'nsfw,racist,sexist,explicit'; //options: explicit,nsfw,political,racist,religious,sexist

  getJokes() {
    function getTypeName(val) {
        return {}.toString.call(val).slice(8, -1);
    }
    
    return this.http
                .get(`${this.apiURL}${this.categories}?blacklistFlags=${this.blacklistFlags}&amount=${this.numJokes}`)
                .pipe(map(data => {
                        const jokesArr = [];
                        for (let i=0; i<10; i++) {
                            jokesArr.push(data['jokes'][i]);
                        }
                        return jokesArr;
                    }))
                .subscribe(data => console.log("Payload = ", getTypeName(data), data)); 
  }
}

//joke-scroll.component.ts
import { Component, OnInit } from '@angular/core';
import { JokesService } from '../../../jokes.service';

@Component({
  selector: 'app-joke-scroll',
  templateUrl: './joke-scroll.component.html',
  styleUrls: ['./joke-scroll.component.sass']
})
export class JokeScrollComponent implements OnInit {

  jokesArr;

  constructor(private jokesService: JokesService) { }

  ngOnInit(): void {
    this.jokesArr = this.jokesService.getJokes();
    console.log(this.jokesArr);
  }

}

//joke-scroll.component.html
<div class='box scrollingText' *ngFor="let joke of jokesArr">
    <p *ngIf=""></p>
    <p *ngIf="joke.type == 'twopart'"> Q: {{ joke.setup }} A: {{ joke.delivery }}</p>
    <p *ngIf="joke.type == 'single'"> {{ joke.joke }} </p>
</div> 

Any help would be appreciated.


Solution

  • I was able to resolve the issue by binding the data and removing the return command at the beginning of the service call. Leaving the pipe function in the service call returned the data as needed. Here is my updated code:

    //joke.service.ts    
    import { Component, OnInit } from '@angular/core';
    import { JokesService } from '../../../jokes.service';
    
    @Component({
      selector: 'app-joke-scroll',
      templateUrl: './joke-scroll.component.html',
      styleUrls: ['./joke-scroll.component.sass']
    })
    
    export class JokeScrollComponent implements OnInit {
    
      jokesArr;
    
      dataReady:boolean = false;
    
      constructor(private jokesService: JokesService) { }
    
      ngOnInit(): void {
        function getTypeName(val) {
            return {}.toString.call(val).slice(8, -1);
        }
    
        this.jokesService.getJokes().subscribe(data => this.jokesArr = data);
                                                    //console.log("Payload = ", getTypeName(data), data);
        //console.log(this.jokesArr);
      }
    
    //joke-scroll.component.ts
    import { Component, OnInit } from '@angular/core';
    import { JokesService } from '../../../jokes.service';
    
    @Component({
      selector: 'app-joke-scroll',
      templateUrl: './joke-scroll.component.html',
      styleUrls: ['./joke-scroll.component.sass']
    })
    
    export class JokeScrollComponent implements OnInit {
    
      jokesArr;
    
      dataReady:boolean = false;
    
      constructor(private jokesService: JokesService) { }
    
      ngOnInit(): void {
        function getTypeName(val) {
            return {}.toString.call(val).slice(8, -1);
        }
    
        this.jokesService.getJokes().subscribe(data => this.jokesArr = data);
                                                    //console.log("Payload = ", getTypeName(data), data);
        //console.log(this.jokesArr);
      }
    
    //joke-scroll.component.html
    <div class='box scrollingText' *ngFor="let joke of jokesArr">
        <p *ngIf="joke.type == 'twopart'"> Q: {{ joke.setup }} A: {{ joke.delivery }}</p>
        <p *ngIf="joke.type == 'single'"> {{ joke.joke }} </p>
    </div> 
    

    Now I just need to fix it so I get one line of 10 scrolling jokes instead of 10 lines of single scrolling jokes. Thanks for the advice peeps.