0

I have two problems, maybe solved with the same answer. I'm getting an error in my console on the front end that it cannot read the propert games of undefined. However my stuff shows up perfectly fine. I'm also having a problem when I'm implementing a pagination element on the frontend/backend where it runs through my function call to the back end twice and screws everything up. I'm assuming that has something to do with the subscribe, but I'm not 100% sure.

I've tried messing with the subscribe, and where I'm calling the get games function, but I can't seem to get it to not throw the error.

This is my service: 
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { environment } from '../../environments/environment';

interface Game {
  name: string;
}

@Injectable({
  providedIn: 'root'
})
  export class GameService {
    private url = environment.gamesAPI;

    httpOptions = {
      headers: new HttpHeaders({'Content-Type': 'application/json'})
    };

    constructor(private http: HttpClient) {}

    getAllGames(page) {
      console.log('11111111111111');
      const queryParams = `?page=${page}`;
      return this.http.get(this.url + queryParams);
    }
  }

This is my component.ts file

export class SportsCardsComponent implements OnInit, AfterViewInit {
  currentPage;
  chart: [];
  gamesArray;

  constructor(
    public games: GameService
  ) { }

  ngOnInit() {
    this.games.getAllGames(this.currentPage).subscribe(game => {
      this.gamesArray = game;
    });
  }

  ngAfterViewInit() {
    this.loadCharts();
  }

  onPageChange(pageData) {
    this.currentPage = pageData;
    this.games.getAllGames(this.currentPage).subscribe(game => {
      console.log(this.currentPage);
      this.gamesArray = game;
    });

    this.loadCharts();

  }

As you can see I'm calling the getAllGames() function on init and when that happens I get an error: ERROR TypeError: Cannot read property 'games' of undefined.

However the games show up fine on my page. Any ideas why I'm getting this error? Is it timing related?

For my second problem, my onPageChange() function calls the same service and I can't seem to get my pagination to work, for some reason it seems to be calling the getAllGames() function twice. I'm assuming it's because I'm subscribing to the call to the db twice?

HTML:

<div class="col-lg-9 float-right"
*ngIf="gamesArray"
>
  <!-- <div *ngIf='loading'  class="d-flex justify-content-center">
    <div class="spinner-border" style="width: 3rem; height: 3rem;" role="status"></div>
  </div> -->
  <!-- <div *ngIf="!loading"> -->
      <div class="row">
          <div class="col-lg-6 card-background"
            *ngFor="let game of gamesArray.games"
          >
            <div class="card-surround shadow-sm">
              <div>
                  <h2>{{game.homeTeam.teamName}}</h2>
                  <h2>{{game.awayTeam.teamName}}</h2>
                  <canvas id="{{game.id}}"></canvas>
                  <hr>
                  <p>{{game.gameTime}}</p>
              </div>
            </div>
        </div>
      </div>
  <!-- </div> -->
  <ngb-pagination
  class="d-flex justify-content-end"
  [collectionSize]="gamesArray.games.length"
  [(page)]="currentPage"
  [maxSize]="5"
  [pageSize]='6'
  (pageChange)='onPageChange($event)'
  size="sm"
  [rotate]="true"
  [ellipses]="false"
  [boundaryLinks]="true"
  ></ngb-pagination>
</div>
Chameleon
  • 119
  • 1
  • 14
  • Wherever `onPageChange` is being called from `this` no longer points to your `SportsCardsComponent`. – Noremac Oct 17 '19 at 20:35
  • @Noremac I'm confused, the number that I'm getting from the front end is correct so clearly it does point to the component. – Chameleon Oct 17 '19 at 20:36
  • @Chameleon your question repeats on stackoverflow numerous like a thousand times a week. in short: you ask your server for data but you USE the variable on your template before it even got a response. so to make things clear, WRAP the element using your server data with an `*ngIf="gamesArray"`. you didn't include your HTML part so that's the best i can give ya here. – Stavm Oct 17 '19 at 20:38
  • @Stavm thanks for the answer, is that best practice, or should I be using an async await? – Chameleon Oct 17 '19 at 20:40
  • a best practice would be to use the `async` pipe. subscribe() should rarely be used with `get` requests. – Stavm Oct 17 '19 at 20:41
  • 1
    Possible duplicate of [How do I return the response from an Observable/http/async call in angular?](https://stackoverflow.com/questions/43055706/how-do-i-return-the-response-from-an-observable-http-async-call-in-angular) – R. Richards Oct 17 '19 at 20:41
  • can you show us your html part – shahidfoy Oct 17 '19 at 21:06
  • @shahidfoy added the HTML in the original – Chameleon Oct 17 '19 at 22:45

2 Answers2

0

Looks like you are calling the service twice possibly because currentPage is undefined than later on it gets defined. I would try setting

currentPage = 1 // or 0 

or whatever the first page is

maybe try to do the following:

ngOnInit() {
  this.onPageChange(1) // or this.onPageChange(0)
}

you could try this in typescript

export class SportsCardsComponent implements OnInit {
  currentPage: number = 1;
  chart: any[];
  gamesArray: any;

  constructor(
    public games: GameService
  ) { }

  ngOnInit() {
    this.onPageChange(this.currentPage);
  }

  onPageChange(pageData: number) {
    console.log('pageData', pageData);
    this.currentPage = pageData;

    this.games.getAllGames(this.currentPage).subscribe((games: any) => {
      console.log('current page', this.currentPage);
      console.log('games', games);
      this.gamesArray = games;
      this.loadCharts();
    });
  }

this might help https://andycostanza.gitlab.io/post/how-pagination-works-with-ng-bootstrap/

if you want you could also create an interface for the gamesArray because I can see that you are calling

  *ngFor="let game of gamesArray.games"

maybe creating an interface for the return type that you get in the service getAllGames then you could probably add an optional property to stop the warning from occurring

https://www.typescriptlang.org/docs/handbook/interfaces.html#optional-properties

shahidfoy
  • 1,951
  • 1
  • 17
  • 23
0

initialize gamesArray with an empty array in the construtor or in the declaration. When the template is rendered, the game service is still processing. At that point your gamesArray property is undefined.

AceVentur4
  • 53
  • 1
  • 1
  • 3
  • This is what I have: constructor( public games: GameService ) { } – Chameleon Oct 18 '19 at 15:30
  • i have a feeling gamesArray is not an array but an object that is returned from the GameService. if it is an array than yes gamesArray = []; should work too – shahidfoy Oct 18 '19 at 21:11