Nagarz
Nagarz

Reputation: 166

Obtaining json and convert it to array in angular2

I've done the angular2 quickstart and the tour of heroes tutorial from the angular site in order to get acquaintanced with it, and after getting it running I felt like the next step was adding a database connection using nodejs and koa to the tour of heroes app to add a little more scalability. I've followed https://github.com/mahalo1984/TourOfHeroesWithMongoDb this tutorial for the most part, changed the db from mongo to mysql and adapted what I needed.

I've upladed my code to https://github.com/nagarz/HeroEditor for the ones who want to see all the code.

I've got the database setup working and I get the query result json formatted, but when I try to pick it up with the getHeroes service which should convert send a promise to the template, I get this

EXCEPTION: Uncaught (in promise): TypeError: Cannot read property 'slice' of undefined

I'm kinda new to JS, and I was getting most of it, and I was able to fix errors given the log, but this one is something I can't get past, this one. I'm not a JS/angular2 wizard, but from what I get the json is not being picked properly by the service and when it tries to convert it into an array and slice it to get X amount of elements from it, it gives the error. I've tried different stuff to get it working but no dice, so I figured I'd ask here hoping that someone better than me (not that hard, feelsbadman) can see what's the problem and point me to it so I can finish the whole setup.

This is the route I use to query the database

router.get('/api/heroes', function* (){ var rows = yield db.query("select * from heroes"); this.body = { heroes: rows }; });

When I enter the url http://localhost:3333/api/heroes in my browser I get the result in json like this

{"heroes":[{"id":1,"hid":1,"name":"Saitama"},{"id":2,"hid":2,"name":"Genos"},{"id":3,"hid":3,"name":"Watchdog Man"},{"id":4,"hid":4,"name":"Metal BAt"},{"id":5,"hid":5,"name":"Puri Puri Prisoner"},{"id":6,"hid":10,"name":"Speed of sound, Sonic"},{"id":7,"hid":11,"name":"Tatsumaki"}]}

And this is where I obtain the response and try to convert it into an array for processing the info in the app.

@Injectable()
export class HeroService {
    private headers = new Headers({'Content-Type': 'application/json'});

    ...
    private getHeroesUrl = 'http://localhost:3333/api/heroes';
    constructor(private http: Http) {}

    //this is the method in question
    getHeroes(): Promise<Hero[]> {
        return this.http.get(this.getHeroesUrl).toPromise().then(response => response.json().data as Hero[]).catch(this.handleError);
    }
}

This is the Hero model class:

export class Hero {
    hid: number;
    name: string;
}

This is the class where method from the service is called, and the obtained array is sliced, and the point where I'm getting the Exception

export class DashboardComponent implements OnInit {
    heroes: Hero[] = [];
    ...
    ngOnInit(): void {
        this.heroService.getHeroes().then(heroes => this.heroes = heroes.slice(1, 4));
    }
    ...

}

This is the full stacktrace of the exception:

Unhandled Promise rejection: Cannot read property 'slice' of undefined ; Zone: angular ; Task: Promise.then ; Value: TypeError: Cannot read property 'slice' of undefined at eval (http://localhost:3000/app/components/dashboard.component.js:22:91) at ZoneDelegate.invoke (http://localhost:3000/node_modules/zone.js/dist/zone.js:203:28) at Object.onInvoke (http://localhost:3000/node_modules/@angular/core/bundles/core.umd.js:6242:41) at ZoneDelegate.invoke (http://localhost:3000/node_modules/zone.js/dist/zone.js:202:34) at Zone.run (http://localhost:3000/node_modules/zone.js/dist/zone.js:96:43) at http://localhost:3000/node_modules/zone.js/dist/zone.js:462:57 at ZoneDelegate.invokeTask (http://localhost:3000/node_modules/zone.js/dist/zone.js:236:37) at Object.onInvokeTask (http://localhost:3000/node_modules/@angular/core/bundles/core.umd.js:6233:41) at ZoneDelegate.invokeTask (http://localhost:3000/node_modules/zone.js/dist/zone.js:235:42) at Zone.runTask (http://localhost:3000/node_modules/zone.js/dist/zone.js:136:47) TypeError: Cannot read property 'slice' of undefined at eval (http://localhost:3000/app/components/dashboard.component.js:22:91) at ZoneDelegate.invoke (http://localhost:3000/node_modules/zone.js/dist/zone.js:203:28) at Object.onInvoke (http://localhost:3000/node_modules/@angular/core/bundles/core.umd.js:6242:41) at ZoneDelegate.invoke (http://localhost:3000/node_modules/zone.js/dist/zone.js:202:34) at Zone.run (http://localhost:3000/node_modules/zone.js/dist/zone.js:96:43) at http://localhost:3000/node_modules/zone.js/dist/zone.js:462:57 at ZoneDelegate.invokeTask (http://localhost:3000/node_modules/zone.js/dist/zone.js:236:37) at Object.onInvokeTask (http://localhost:3000/node_modules/@angular/core/bundles/core.umd.js:6233:41) at ZoneDelegate.invokeTask (http://localhost:3000/node_modules/zone.js/dist/zone.js:235:42) at Zone.runTask (http://localhost:3000/node_modules/zone.js/dist/zone.js:136:47)

Upvotes: 1

Views: 776

Answers (2)

Rob
Rob

Reputation: 81

The in-memory-web-api module has a breaking change:

As of v0.5.0 (5 October 2017), the dataEncapsulation configuration default changed from false to true. The HTTP response body holds the data values directly rather than an object that encapsulates those values, {data: ...}. This is a breaking change that affects almost all existing apps!

So, in the example, change this:

  getHeroes(): Promise<Hero[]> {
    return this.http.get(this.heroesUrl)
      .toPromise()
      .then(response => response.json().data as Hero[])
      .catch(this.handleError);
  }

to:

  getHeroes(): Promise<Hero[]> {
    return this.http.get(this.heroesUrl)
      .toPromise()
      .then(response => response.json() as Hero[])
      .catch(this.handleError);
  }

Upvotes: 4

Ross Edfort
Ross Edfort

Reputation: 306

Looks like you are expecting an array to be returned but the data is actually an Object. Try this

getHeroes(): Promise<Hero[]> {
  return this.http.get(this.getHeroesUrl).toPromise().then(response => response.json().data.heroes as Hero[]).catch(this.handleError);
}

your hero array is nested under a 'heroes' key in a JSON data structure.

Upvotes: 2

Related Questions