Getting undefined value from another function despite setting public variable


#1

I really hope someone can help me out with this one cause I can’t figure out what is wrong with my logic.

In my TS file I have a simple function like this to generate eventid

public eventid: number;
  createEventId(){
    $.get("http://localhost:8000/getEvents.php", (data)=>{
      var obj1 = jQuery.parseJSON(data);
      for (var i = 0; i < obj1.length; i++){
          var eventid = parseInt(obj1[i].event_id);
          if (eventid < 1){
            eventid = 1;
          } else {
            eventid = eventid + 1;
          }
      }
      return eventid;
    });
  }

I tried logging out eventid when it was returned and got a value of 5 (int), which was expected.

However, when I tried to use that value in the next function as following, it gave me undefined value.

  eventname: string;
  eventdate = new Date().toISOString();
  createEvent(){
    this.createEventId();
    var eventname = this.eventname;
    var eventdate = this.eventdate;
    console.log(eventname);
    console.log(eventdate);
    console.log(this.eventid);
  }

I have declared eventid as a public variable so it should be accessible from anywhere right? Please tell me what I’m missing. Thank you all.


#2

I know I’m a broken record, but I’d definitely recommend using Angular HTTP in this case as it’ll make it easier.

Switching over requires adding the import:
import { Http } from '@angular/http';

Then putting it into your constructor:
constructor(..., http: Http, ...)

Then we update the createEventId function:

  createEventId() {
    this.http.get("http://localhost:8000/getEvents.php").toPromise().then(data => {
      for (var i = 0; i < data.length; i++){
          var eventid = parseInt(data[i].event_id);
          if (eventid < 1){
            eventid = 1;
          } else {
            eventid = eventid + 1;
          }
      }
      return eventid;
    });
  }

Great, now we’re using Angular’s HTTP. (Also notice that I removed the JSON parsing call, as that should be unnecessary when using Angular)

Now, onto the problem you’re having. There are two issues.

The first one is that you have 2 eventid variables declared, and you only ever set the one defined in your function and never the public property.

The second is that createEventId is asynchronous, and you’re trying to use it synchronously.

I’d propose changing things around to this:

  getEventId() {
    return this.http.get("http://localhost:8000/getEvents.php").toPromise().then(data => {
      for (var i = 0; i < data.length; i++){
          var eventid = parseInt(data[i].event_id);
          if (eventid < 1){
            eventid = 1;
          } else {
            eventid = eventid + 1;
          }
      }
      return eventid;
    });
  }

This changes it to getEventId (to better express what it’s doing), and has it return a Promise.

Now we can change createEvent to:

  createEvent(){
    this.getEventId().then(eventid => {
      var eventname = this.eventname;
      var eventdate = this.eventdate;
      console.log(eventname);
      console.log(eventdate);
      console.log(eventid);
    });
  }


#3

Hi Sigmund, thanks very much for your explanation.

I have made changes as you instructed. My current code is as following:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { HomePage } from '../home/home';
import { CreateTaskPage } from '../create-task/create-task';
import { DatePicker } from '@ionic-native/date-picker';
import { Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
/**
 * Generated class for the CreateEventPage page.
 *
 * See https://ionicframework.com/docs/components/#navigation for more info on
 * Ionic pages and navigation.
 */

@Component({
  selector: 'page-create-event',
  templateUrl: 'create-event.html',
  providers: [DatePicker],
})
export class CreateEventPage {
 
  constructor(
    public navCtrl: NavController, 
    public navParams: NavParams,
    private http: Http, 
    private datePicker: DatePicker) {
  }

  // list of doers is populated when screen is loaded
  ionViewDidLoad() {
    console.log('ionViewDidLoad CreateEventPage');
  }

  goToTask() {
    this.navCtrl.push(CreateTaskPage);
  }

  // check if any event already exists, if not generate a new eventid
  public eventid: number;
  getEventId() {
    return this.http.get("http://localhost:8000/getEvents.php").toPromise().then(data => {
      for (var i = 0; i < data.length; i++){
          var eventid = parseInt(data[i].event_id);
          console.log(eventid);
          if (eventid < 1){
            eventid = 1;
          } else {
            eventid = eventid + 1;
          }
      }
      // console.log (eventid);
    });
  }
  
  // prepare data for creating an event
  eventname: string;
  eventdate = new Date().toISOString();
  createEvent(){
     this.getEventId().then(eventid => {
      var eventname = this.eventname;
      var eventdate = this.eventdate;
      console.log(eventname);
      console.log(eventdate);
      console.log(eventid);
    });
  }
}

The app shows up fine when run ionic lab. However when I tried to print out eventid I still got undefined.
Did I miss any step, please let me know? Thanks again


#4

Apologies, busy weekend.

It looks like you’re not returning eventid from getEventId, but to take a step back what is that function supposed to be doing? My guess is that it’s supposed to be finding the largest existing event id, then adding 1 to get the new event id, aye?

If so, you should be able to re-write it as:

getEventId() {
  return this.http.get("http://localhost:8000/getEvents.php").toPromise().then(data => {
    return data.reduce((large, event) => {
      let eventId = parseInt(event.event_id);
      return eventId > large ? eventId : large;
    }, 0) + 1;
  });
}

So the next thing to note is that you can’t print eventid until after the promise completes.

So this is safe to do:

this.getEventId().then(id => {
  console.log('my new event id', id);
});

But this is not:

let eventid = 0;
this.getEventId().then(id => eventid = id);
console.log('the following will be undefined :(', eventid);

Another thing to keep in mind is that with the set-up I’ve proposed you are no longer using the public eventid property.

Also also, you don’t normally want to put providers on components themselves. Instead you just add them to the providers list in app.module.ts.
(In particular I’m referring to the providers: [DatePicker], line)