0

I'm wrestling with an undefined return from a promise. From my detective work, it seems that the problem is assigning the results from the promise to a instance variable on the component where I am calling the promise. I think it is a type(or similar) problem with the arrow function where I try to assign the returned survey to this.survey.

Any insight would be appreciated!

./survey.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { SURVEYS } from './mock-survey';

export class Option {
constructor(public id: number,
          public text: string,
          public value: number
        ){}
}

export class Question {
constructor(public id: number,
          public type: string,
          public text: string,
          public required: boolean,
          public options: Option[] = []
        ){}
}

export class Survey {
constructor(public id: number,
          public name: string,
          public description: string,
          public instructions: string,
          public questions: Question[] = []
        ){}
}

@Injectable()
export class SurveyService {

  getSurveys(): Promise<Survey[]> {
    return Promise.resolve(SURVEYS);
  }

  getSurvey(id: number | string): Promise<Survey> {
    return this.getSurveys()
      .then(surveys => surveys
      .find(survey => survey.id === +id));
  }

  public observable: Observable<any>;
}

./survey.component.ts

import 'rxjs/add/operator/switchMap';
import { Observable } from 'rxjs/Observable';
import { Component, OnInit, Inject, HostBinding }      from '@angular/core';
import { MdDialogRef, MD_DIALOG_DATA }  from '@angular/material';
import { Survey, SurveyService } from '../survey.service';

@Component({
  selector: 'app-survey',
  templateUrl: './survey.component.html',
  styleUrls: ['./survey.component.scss'],
  providers: [SurveyService]
})
export class SurveyComponent implements OnInit {

  survey: Survey
  surveyResults: {}

  constructor(
    private surveyService: SurveyService,
    public dialogRef: MdDialogRef<SurveyComponent>,
    @Inject(MD_DIALOG_DATA) public data: any
  ) { }

  // getSurveys(): void {
  //   this.surveyService.getSurveys().then(
  //     surveys => this.surveys= surveys);
  // }


  // *This method also returns an undefined this.survey; Not issue with ngOnInit()
  // getSurvey(): void {
  //   let survey_id = this.data.survey_id
  //   this.surveyService
  //       .getSurvey(survey_id)
  //       .then(s => {this.survey = s});
  // }

  ngOnInit(): void {
     this.surveyService
      .getSurvey(this.data.survey_id)
      .then((survey: Survey) => {this.survey = survey});

    // returns survey_id from MD_DIALOG_DATA
    console.log(this.data.survey_id)
    // returns survey JSON object; This arrives in browser console
    console.log(this.surveyService.getSurvey(this.data.survey_id));
    // returns undefined
    console.log(this.survey);
  }

  confirmSurveyResults() {
    this.dialogRef.close(this.surveyResults);
    console.log(this.survey);
  }


}
Mitt
  • 1
  • 1
  • 1
  • Can you provide the content of `mock-survey` file, please ? – ADreNaLiNe-DJ Jul 28 '17 at 14:32
  • Possible duplicate of [How do I return the response from an Observable/http/async call in angular2?](https://stackoverflow.com/questions/43055706/how-do-i-return-the-response-from-an-observable-http-async-call-in-angular2) – AT82 Jul 28 '17 at 14:38
  • 1
    Possible duplicate of [Response of Http Api call promise is undefined - Angular 2](https://stackoverflow.com/questions/45328860/response-of-http-api-call-promise-is-undefined-angular-2) – CodeWarrior Jul 28 '17 at 14:41
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Igor Jul 28 '17 at 14:44
  • Thanks for quick responses. I understand now why it will return undefined within the ngOnInit function. – Mitt Jul 29 '17 at 21:07
  • But, I'm still having an issue. The arrow function assignment is not working outside the ngOnit. The survey object is not available in the template. When I add `*ngIf="survey"` if returns falsy and I get a blank template. If I remove the *ngIf="survey and try to access `{{survey.name}}`, then I get a `ERROR TypeError: Cannot read property 'name' of undefined`. This seems like an angular issue. I'm probably missing a detail somewhere. Thoughts? – Mitt Jul 29 '17 at 21:31

2 Answers2

1

Promises are asynchronous, so the survey will be always undefined within the ngOnInit method's context:

  ngOnInit(): void {
     this.surveyService
      .getSurvey(this.data.survey_id)
      .then((survey: Survey) => {
         this.survey = survey
         console.log(this.survey);// here it WILL NOT be undefined
       });


    // returns undefined because the promise is not fulfilled yet
    console.log(this.survey);
  }
Pablo Lozano
  • 10,122
  • 2
  • 38
  • 59
  • Thanks for response and clarification. That makes sense now why it returns undefined inside ngOnInit. But, I'm still having an issue. The arrow function assignment is not working outside the ngOnit. The survey object is not available in the template. When I add `*ngIf="survey"` if returns falsy and I get a blank template. If I remove the *ngIf="survey and try to access `{{survey.name}}`, then I get a `ERROR TypeError: Cannot read property 'name' of undefined`. This seems like an angular issue. I'm probably missing a detail somewhere. Thoughts? – Mitt Jul 29 '17 at 21:32
0

Like said, the survey isn't there yet on init. When you load the html it still won't be there, so you'll have to make sure the survey exists before using it in things like ngfor.

Perhaps use ngIf?

Jusmpty
  • 171
  • 1
  • 8