Unable to access the response in html file


#1

I was trying to call a third party api and it seems to be working fine in the class
Following is my provider class

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

/*
  Generated class for the EventService provider.

  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
  for more info on providers and Angular 2 DI.
*/
@Injectable()
export class EventService {
  public data: any;
  constructor(public http: Http) {
    console.log('Hello EventService Provider');
  }

  loadEvents() {
    if (this.data) {
      // already loaded data
      return Promise.resolve(this.data);
    }

    // don't have the data yet
    return new Promise(resolve => {
      // We're using Angular HTTP provider to request the data,
      // then on the response, it'll map the JSON data to a parsed JS object.
      // Next, we process the data and resolve the promise with the new data.
      this.http.get('/api/events')
        .map(res => res.json())
        .subscribe(data => {
          // we've got back the raw data, now generate the core schedule data
          // and save the data for later reference
          this.data = data;
          resolve(this.data);
        });
    });
  }

  loadEventDetail(eventId){
    if (this.data) {
      return Promise.resolve(this.data);
    }

    return new Promise(resolve => {
      this.http.get('/api' + '/events/' + eventId )
        .map(res => res.json())
        .subscribe(data => {
          this.data = data;
          console.log(data);
          resolve(this.data);
        });
    });
  }
}

In my Page module class i have called this service for getting the service and was able to view the same in console log

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import {EventService} from '../../providers/event-service';
/**
 * Generated class for the EventDetail page.
 *
 * See http://ionicframework.com/docs/components/#navigation for more info
 * on Ionic pages and navigation.
 */
@IonicPage()
@Component({
  selector: 'page-event-detail',
  templateUrl: 'event-detail.html',
  providers: [EventService]
})
export class EventDetailPage {
  public eventId: any;
  public event: any;

  constructor(public navCtrl: NavController, public navParams: NavParams, public eventService: EventService) {
    this.eventId = navParams.get('eventId');
    this.loadEventDetails(this.eventId);
  }

 loadEventDetails(eventId){
    this.eventService.loadEventDetail(eventId)
    .then(data => {
      this.event = data;
      console.log(this.event.title);
    });
  }


}

But when i am trying to acces the event object in my html file like this

<ion-header>

  <ion-navbar color="danger">
    <ion-title>Event Details</ion-title>
  </ion-navbar>

</ion-header>


<ion-content padding>
  <p>{{ eventi.title}}</p>
</ion-content>

I am getting an error saying ,
Runtime Error
Uncaught (in promise): TypeError: Cannot read property ‘title’ of undefined

THis is the json response

{"id":2,"title":"Meetup - Ruby On Rails with React.js","description":"A meetup will be held at technopark Thejaswini building , organised by Weavelabs","date":"2017-05-25T06:05:34.401Z","event_type":"FREE","street":"Kazhakootam","city":"Trivandrum","state":"Kerala","country":"India","latitude":1.094775,"longitude":-1.09878378,"contact_details":[{"id":2,"primary_number":"9562141330","email":"manu@yahoo.in","secondary_number":"978645321"}],"photos":[],"category":{"id":2,"name":"Technical"}}

Another api listing events is working fine and i am not sure whether i am doing anything wrong.Tried almost every possible way but seems to be not working.
I just moved to Ionic 3
ANy help will be appreciated


#2

Hmmm is the eventi a typo or?

should be just event.title

also in the loadEventDetails, why dont you just return a observable back to the DetailsPage and then subscribe to it there?


Fetch data from nested json
#3

@kgaspar Its a typo actually.Not the reason for the issue. I am new to ionic 3 and just followed a tutorial to get this done. I follwed the syntaxover there.It seems to be working with one of my api and ionic view pages.Not sure why this happening.The other page was created by using the tabs template by default.
This page i created using ionic g page <page>


#4

Well as seen in your code, you have a variable data that stores the events that you get from your api. Now, storing the events in a variable in your provider is a good way to share it between the pages of your app without the need to fetch the data constantly but manage the data changes in the provider. Thats good practice.

Now what I want to say is, you save the events and eventDetails to the same variable data

Now you have two functions loadEvents() and loadEventDetails() that both fetch some data and save it to the same variable.

Now imagine the situation where you first call the loadEvents function, it saves the list of events that you are fetching.
Next you call the loadEventsDetails() function it checks

    if (this.data) {
      return Promise.resolve(this.data);
    }

but the data variable is populated by the loadEvents function so it will just return the data that is actually the list of events and not the event details. And that variable doesnt have the title atribute and it trows an error.

The solution would be

loadEventDetail(eventId){
     return this.http.get('/api' + '/events/' + eventId ) .map(res => res.json());
}

and then in the page component subscribe to the returned Observable.


#5

@kgaspar Thanks for your detailed explanation.I appreciate that.I changed the code as you said and it seems to be not working here in my case.Following is the updated version.

event-detail.ts

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import {EventService} from '../../providers/event-service';
/**
 * Generated class for the EventDetail page.
 *
 * See http://ionicframework.com/docs/components/#navigation for more info
 * on Ionic pages and navigation.
 */
@IonicPage()
@Component({
  selector: 'page-event-detail',
  templateUrl: 'event-detail.html',
  providers: [EventService]
})
export class EventDetailPage {
  public eventId: any;
  public event: any;

  constructor(public navCtrl: NavController, public navParams: NavParams, public eventService: EventService) {
    this.eventId = navParams.get('eventId');
    this.eventService.loadEventDetail(this.eventId)
    .subscribe(data => {
      this.event = data;
    });
  }


}

event-service.ts

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

/*
  Generated class for the EventService provider.

  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
  for more info on providers and Angular 2 DI.
*/
@Injectable()
export class EventService {
  public data: any;
  constructor(public http: Http) {
    console.log('Hello EventService Provider');
  }

  loadEvents() {
    if (this.data) {
      // already loaded data
      return Promise.resolve(this.data);
    }

    // don't have the data yet
    return new Promise(resolve => {
      // We're using Angular HTTP provider to request the data,
      // then on the response, it'll map the JSON data to a parsed JS object.
      // Next, we process the data and resolve the promise with the new data.
      this.http.get('/api/events')
        .map(res => res.json())
        .subscribe(data => {
          // we've got back the raw data, now generate the core schedule data
          // and save the data for later reference
          this.data = data;
          resolve(this.data);
        });
    });
  }

  loadEventDetail(eventId){
    return this.http.get('/api' + '/events/' + eventId ) .map(res => res.json());
  }
}

View

<ion-header>

  <ion-navbar color="danger">
    <ion-title>Event Details</ion-title>
  </ion-navbar>

</ion-header>


<ion-content padding>
  <p>{{ event.title }}</p>
</ion-content>

#6

Still the same error? Does it successfully fetch the event details data?


#7

Yes in the log it shows the json


#8

can you print the data variable here.

this.eventService.loadEventDetail(this.eventId)
    .subscribe(data => {
      this.event = data;
      console.log(data);
    });

#9

@kgaspar


#10

Hmmm might be a problem since we forgot to initialise the event data.

Try

public event = {};

in your EventDetailsPage


#11

I have already defined
public event: any;
when i changed to
public event = {} ;
it works.So may i know the difference between two declarations.


#12

Well

public event: any;

is a type declaration. It basicly says that you can store any type of data in the variable event. That in this context doesnt have much sense, but consider this:

public message: string;

and in some other function you do something like:

this.message = 3;

it would trow an error since the variable message expects a string and not a number. This helps you keep your code clean, reusable and less prone to errors.

We were getting a event.title not found error, because basicly we never instanced any variable. By doing public event = {} we assigned an actual value to the variable.


#13

I agree.But in my other api which i get a list of events i defined the same like
public events: any;

and accessed the same like


<ion-content>
    <ion-item text-wrap *ngFor="let event of events">
      <ion-card>
        <img src="http://ionicframework.com/img/docs/delorean.jpg"/>
        <ion-card-content>
          <ion-card-title>
            {{ event.title }} <ion-badge color="danger"> {{ event.date | date : "MMMM d, yyyy - HH:mm"}}</ion-badge>
          </ion-card-title>
          <p>
            {{ event.description }}
          </p>
          <button ion-button full color="danger" (click)="eventDetail(event.id)">View More Details</button>
        </ion-card-content>
      </ion-card>
    </ion-item>
</ion-content>

In the above case it’s working fine and when comes to second api when i followed the same syntax it doesn’t worked.
I assume using let in the first case saved me.
Which is not used in the second declaration.TIme to learn more angular and Typescript.I am from a Ruby on Rails background.

@kgaspar Thanks for your time and effort. Almost wasted a day on this, just started on ionic… Will Keep posting my silly doubts :yum:


#14

Well in this case, since events is a array of events that you will get by a async http function, you should declare the events array

public events = [];```

since its empty when the app starts the ngFor will render nothing, after the asnyc http call is done it will fetch the new events array, when its done the ngFor will render the new array since its not empty.

#15

Got it .Thanks pal :blush:


#16

Please, nobody emulate the provider code in the first post. It is antipattern soup.

// avoid name collision with eslib Event
export interface Happening {
}

@Injectable()
export class HappeningService {
  private _happenings: Promise<Happening[]>;

  constructor(private _http: Http) {
  }

  allHappenings(): Promise<Happening[]> {
    if (!this._happenings) {
      this._happenings = this._http.get('/api/events')
        .map(rsp => rsp.json())
        .toPromise();
    }
    return this._happenings;
  }

  clearHappeningsCache(): void {
    this._happenings = null;
  }
}

#17

@rapropos May i know the reason why its antipattern?I just followed a tutorial


#18

Here are some links and more explication of the trouble with that code.


#19

@rapropos I tried your code and it is working perfectly.But showing a warning in console

Cannot find name 'Happening'. 

      L19:    allHappenings(): Promise<Happening[]> {
      L20:      if (!this._happenings) {

            Cannot find name 'Happening'. 


#20

Happening is an interface that contains all the fields present in your business logic class representing an event. If you look back at my post, you should see it being declared as an empty interface. Start with that and add fields to it. TypeScript interfaces.