2

I'm trying to display/loop a variable(s) in an array by using the following piece of code.

<tr *ngFor="let ep of theshow.episodes[0]">

Unfortunately looks like angular does not allow me to do this, is there a way I would be able to only show the array inside the array with index 0?

Fyi: This is my first tryout personal project to learn angular 2, so if you see anything else that might be done in a better way, please do tell.

The data in "theshow" variable is the following (google chrome inspector copy/paste)

Object
  name: "game of thrones"
  airtime: "21:00"
  id: 82
  episodes: Array[7]    //each array contains a season
    0: Array[10]    //each season contains all there episodes (this is season 1)
        0: Object   //episode 1
            name: "Winter is coming"
            ...
        1: Object   //episode 2
            name: "The Kingsroad"
            ...
        2: Object   //episode 3
            name: "Lord Snow"
            ...
    1: Array[10] //season 2
        0: Object   //episode 11
            name: "The North Remembers"
            ...
        1: Object   //episode 12
            name: "The Night Lands"
            ...
        2: Object   //episode 13
            name: "What is Dead May Never Die"
            ...
    ...

this is the full piece of code I try to run is.

<tr *ngFor="let ep of theshow.episodes[0]">
  <td class="mailbox-name">{{ep.name}}</td>
  <td class="mailbox-subject">{{ep.airdate}}</td>
  <td class="mailbox-date">{{ep.airtime}}</td>
</tr>

Error message I receive is:

Uncaught (in promise): Error: Error in ./SearchComponent class    SearchComponent - inline template:69:22 caused by: Cannot read property '0' of undefined
TypeError: Cannot read property '0' of undefined
at _View_SearchComponent2.detectChangesInternal (/AppModule/SearchComponent/component.ngfactory.js:555:61)
at _View_SearchComponent2.AppView.detectChanges (http://127.0.0.1:4200/main.bundle.js:56393:14)
at _View_SearchComponent2.DebugAppView.detectChanges (http://127.0.0.1:4200/main.bundle.js:56498:44)
at _View_SearchComponent0.AppView.detectContentChildrenChanges (http://127.0.0.1:4200/main.bundle.js:56411:19)
at _View_SearchComponent0.detectChangesInternal (/AppModule/SearchComponent/component.ngfactory.js:81:8)
at _View_SearchComponent0.AppView.detectChanges (http://127.0.0.1:4200/main.bundle.js:56393:14)
at _View_SearchComponent0.DebugAppView.detectChanges (http://127.0.0.1:4200/main.bundle.js:56498:44)
at _View_SearchComponent_Host0.AppView.detectViewChildrenChanges (http://127.0.0.1:4200/main.bundle.js:56419:19)
at _View_SearchComponent_Host0.detectChangesInternal (/AppModule/SearchComponent/host.ngfactory.js:33:8)
at _View_SearchComponent_Host0.AppView.detectChanges (http://127.0.0.1:4200/main.bundle.js:56393:14)

serie.service.ts

import { Injectable, Input } from '@angular/core';
import { Http, Response, RequestOptions, Headers } from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class SerieService {
  url = "//api.tvmaze.com/search/shows?q=";

  getSerie(id){
    this.url = "http://api.tvmaze.com/shows/"+ id +"?embed=episodes";
    var re = '';
    return this._http.get(this.url).map(this.cleanSerieData);
  }

  private cleanSerieData(res: Response){
    let body = res.json();
    var tmp = [];
    body._embedded.episodes.forEach(el => {
      if(tmp[el.season-1] == undefined){
        tmp[el.season-1] = [];
      }
      tmp[el.season-1].push(el);
    });
    var clean = {
      id: body.id,
      name: body.name,
      rating: body.rating.average,
      airday: body.schedule.days,
      airtime: body.schedule.time,
      status: body.status,
      summary: body.summary,
      episodes:tmp,
      countSeasons: tmp.length,
      countEpisodes: body._embedded.episodes.length
    };
    return clean;
  }

  constructor(private _http:Http) { }

}

Search.component.ts

import { Component, OnInit } from '@angular/core';
import { SerieService } from './../serie.service';
import { Router, ActivatedRoute, Params } from '@angular/router';

@Component({
  selector: 'app-search',
  templateUrl: './search.component.html',
  styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnInit {
  srh = '';
  shows = [];
  tmp = [];
  id = undefined;
  theshow = {};

  search(event:any){
    this.srh = event.target.value;

    this.serieService.getSeries(this.srh)
      .subscribe(res => {
        console.log(res);
        this.shows = res;
      })
  }

  constructor(
    private serieService: SerieService,
    private route: ActivatedRoute,
    private router: Router
  ) { }

  ngOnInit() {
    this.id = this.route.snapshot.params['id'];
    this.searchFunction();
  }



  searchFunction()
  {
    if(this.id == undefined)
    {
      console.log('search functie');
    }else{
      this.serieService.getSerie(this.id).subscribe(res => {
        console.log(res);
        this.theshow = res;
        console.log(res.episodes[0])
      });
    }
  }

}

Edit, added code.

Eventually the idea is that I can click on season 2 (button) and get all the episodes of season 2 by changing to (using a variable ofc)

Many thanks for your response.

4
  • What error are you getting? Commented Nov 1, 2016 at 21:22
  • Thanks for responding. error I'm getting is. Uncaught (in promise): Error: Error in ./SearchComponent class SearchComponent - inline template:69:22 caused by: Cannot read property '0' of undefined TypeError: Cannot read property '0' of undefined Commented Nov 1, 2016 at 21:34
  • That just means that theshow.episodes is undefined. It's not an array as you think it is. At least not at the moment this expression is first evaluated. Maybe the episodes are loaded asynchronously. Post your code or more help. Commented Nov 1, 2016 at 21:45
  • I added my serie.service.ts and search.component.ts files, I do get my data asynchronously although it does not really have to be since the data wont change. (I only learned the asynchronously from tutorials online :/ ) Commented Nov 1, 2016 at 22:13

2 Answers 2

2

The answer by Stefan is close, but missing the key information from the updated post.

The asynchronous fetch does, indeed, mean that the episodes property on theshow variable is not yet defined.

There are three possible solutions with what you have built so far:

  1. Changing the initialization of theshow variable from theshow = {} to theshow = {episodes: []}.
  2. Check that episodes exists before rendering. The guard isn't working in my plunker, but <table *ngIf="theshow?.episodes"><tr *ngFor="let episode of theshow.episodes[0]"> that is working (theshow?.episodes?[0] does not appear to be valid syntax).
  3. Creating appropriate objects that you can instantiate as dummies theshow: ShowInformation = new ShowInformation() where you have appropriately defined the ShowInformation class and it correctly constructs empty variables as placeholders.
  4. Use the async pipe such as in this QA, but it requires more rework for probably a better long term solution: Problems using async pipe with ngFor in Angular2

Here's a hardwired plunker example (I didn't bother to implement the search, routing, etc): http://plnkr.co/edit/i0Vx6cLmPyNyavLCwCeY?p=preview

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

1 Comment

Thanks! Makes sense, I'll give it a try tomorrow morning
0

You can do it like below:

<tr *ngFor="let ep of theshow.episodes">
  <td class="mailbox-name">{{ep[0].name}}</td>
  <td class="mailbox-subject">{{ep[0].airdate}}</td>
  <td class="mailbox-date">{{ep[0].airtime}}</td>
</tr>

Comments

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.