【问题标题】:angular/ts global variable assigned in async call undefined in constructor在异步调用中分配的角度/ ts全局变量在构造函数中未定义
【发布时间】:2018-01-25 19:54:23
【问题描述】:

我正在使用对我的 Web API 的 HTTP 调用来检索 2 个 API 密钥以使用另一个 API。

这些 API 密钥通过 2 个函数检索: getApiKey()getAppId()

当我在构造函数中调用这些函数时,它们返回的值是一个全局变量,是未定义的。

当我在构造函数之外调用它时,它工作正常。

我不想使用全局变量,但是当我尝试在 getApiKey()getAppid() 函数体内创建变量并在 http.get 调用中分配它时,它也返回 undefined。

我猜这与 http.get 是异步的有关,但我不知道如何修复它/如何让它等待响应。

这是我的代码:

import { Component, OnInit } from '@angular/core';
import { Http, Headers, Response, RequestOptions } from '@angular/http';
import { Constants } from '../../utils/constants';
import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-recipes',
  templateUrl: './recipes.component.html',
  styleUrls: ['./recipes.component.css']
})
export class RecipesComponent {
  appid;  
  appkey;
  matchesList;  
  recipeSearchForm: FormGroup;
  notFoundError: boolean = false;

  constructor(private http: Http) {
    this.searchRecipeInit();    //undefined here

    this.recipeSearchForm = new FormGroup({
      recipeSearchInput: new FormControl()
    });
  }

  getApiKey(){
    this.http.get(Constants.GET_YUMMLY_APP_KEY, this.getOptionsSimple()).subscribe((res: Response) => {  
      this.appkey = res.text();
      console.log(this.appkey);   
    });
    return this.appkey;    
  }

  getAppId(){
    this.http.get(Constants.GET_YUMMLY_APP_ID, this.getOptionsSimple()).subscribe((res: Response) => {  
      this.appid = res.text(); 
      console.log(this.appid);      
    });
    return this.appid;    
  } 

  getSearchParams(){
    // get from search text field
    var str = this.recipeSearchForm.get('recipeSearchInput').value
    // split into words and add + in between
    if(str != null) {
      var correctFormat = str.split(' ').join('+');
      return correctFormat          
    }
    return str
  }

  getOptions(){
    var headers = new Headers();

    headers.append('Content-Type', 'application/json' );    
    headers.append('X-Yummly-App-Key',this.getApiKey());    
    headers.append('X-Yummly-App-ID',this.getAppId());

    let options = new RequestOptions({ headers: headers });

    return options;
  }

  getOptionsSimple(){
    var headers = new Headers();

    headers.append('Content-Type', 'application/json' ); 

    let options = new RequestOptions({ headers: headers });

    return options;
  }

  searchRecipe() {     
      // not undefined here
      this.http.get(Constants.GET_SEARCH_RECIPE+this.getSearchParams(), this.getOptions()).subscribe((res: Response) => {  
        this.matchesList = res.json().matches;

        console.log(this.matchesList);
        if(this.matchesList.length == 0){
          this.notFoundError = true;
        }
        else{
          this.notFoundError = false;
        }
      },
      (err) => {
        if(err.status == 400){
          // Bad Request
        }
        else if(err.status == 409){
          // API Rate Limit Exceeded
        }
        else if(err.status == 500){
          // Internal Server Error
        }
      });
  }

  searchRecipeInit() {     
    this.http.get(Constants.GET_SEARCH_RECIPE+"", this.getOptions()).subscribe((res: Response) => {  
      this.matchesList = res.json().matches;

      this.notFoundError = false;      
    },
    (err) => {
      if(err.status == 400){
        // Bad Request
      }
      else if(err.status == 409){
        // API Rate Limit Exceeded
      }
      else if(err.status == 500){
        // Internal Server Error
      }
    });
}
}

【问题讨论】:

    标签: angular typescript asynchronous


    【解决方案1】:

    您包含的代码按预期工作。这个问题可能与How do I return the response from an asynchronous call? 重复,但因为它更多是关于可观察的,所以我会回答它。

    你对异步代码的误解是主要问题,具体如下代码

    getAppId() {
        this.http.get(Constants.GET_YUMMLY_APP_ID, this.getOptionsSimple()) // 1
            .subscribe((res: Response) => {                                 // 
                this.appid = res.text();                                    // 3
                console.log(this.appid);                                    // 4
            });
        return this.appid;                                                  // 2
    } 
    

    代码按右侧标记的编号顺序执行。由于 TypeScript/JavaScript 是同步的,它将触发 http.get(...) 函数,然后继续下一行,在本例中为 return this.appid;。此时this.appid 有什么价值? undefined,因此它按预期工作。

    您必须返回 http.get(...) 的结果,该结果在调用 .subscribe() 函数之前不可用。

    由于您依赖于两个单独的http.get(...) 调用,一个用于apiKey,一个用于appId,您可以利用Rx 运算符“等待”它们完成/发出值。在这种情况下,想到的是Observable.zip() 函数。

    我创建了一个 sn-p,它可以引导您朝着正确的方向前进,让您的代码按预期工作。

    class SearchRecipeDemo {
    
      private getAppId() {
        return this.http.get(Constants.GET_YUMMLY_APP_ID);
      }
    
      private getApiKey() {
        return this.http.get(Constants.GET_YUMMLY_APP_KEY);
      }
    
      private init(): void {
        this.searchRecipe();
      }
    
      private getOptions() {
        return Rx.Observable.zip(getApiKey(), getAppId()).map((result) => {
            // Prints your keys
            console.log(`apiKey: ${result[0].text()}`);
            console.log(`appId: ${result[1].text()}`);
    
            // Create the RequestOptions
            let headers = new Headers();
    
            headers.append('Content-Type', 'application/json');
            headers.append('X-Yummly-App-Key', result[0].text());
            headers.append('X-Yummly-App-ID', result[1].text();
    
              const options = new RequestOptions({
                headers: headers
              });
              return options;
            });
        });
    
      private searchRecipe() {
        this.getOptions().map((options) => {
            // Options here is the RequestOptions object returned from the 'getOptions' function
            console.log(options);
    
            //Make your request to search the recipe here
          })
          .subscribe();
      }
    }
    
    new SearchRecipeDemo().init();
    

    看看这个JSBin JSBin sn-p 中模拟可观察对象的代码版本略有不同。

    【讨论】:

    • 感谢您的详细回复!我了解其中的大部分内容,唯一我仍然不太了解的是在这种情况下我应该如何将我的 appkey 和 appid 绑定到我的 http 调用的响应。我是否在 getOptions 正文中进行 http 调用?
    • 我已经用稍微不同的代码 sn-p 更新了我的答案,它与您的应用程序更匹配。我用调用 getApiKey()getAppId() 函数替换了模拟的 observables。请注意,我还更改了这些函数以从 http.get() 调用返回 observable。我真的不明白为什么你有getOptionsSimple(),所以我省略并重构了代码。
    猜你喜欢
    • 1970-01-01
    • 2019-04-26
    • 2016-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-28
    • 2018-09-16
    相关资源
    最近更新 更多