【问题标题】:Instance variable not assigned in Observable<> subscribe?实例变量未在 Observable<> 订阅中分配?
【发布时间】:2019-07-13 17:19:49
【问题描述】:

从 Obersavable 订阅时,出于某种原因,我的游戏数组没有被分配,即使我确实从服务器获得了正确的数据(数组)。

这是我的 game.service.ts:

import { Injectable } from '@angular/core';
import { Game } from '../models/game';
import { of, Observable } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';
import { NGXLogger } from 'ngx-logger';

@Injectable({
  providedIn: 'root'
})
export class GameService {
  private gamesUrl: string = 'api/games';

  constructor(private http: HttpClient, private logger: NGXLogger) { }

  getGames(): Observable<Game[]> {
    return this.http.get<Game[]>(this.gamesUrl);
  }

  getGame(id: number): Observable<Game> {
    const url: string = (`${this.gamesUrl}/${id}`);
    return this.http.get<Game>(url).pipe(tap(_ => this.logger.debug(`fetched game id=${id}`)), catchError(this.handleError<Game>(`getGame id=${id}`)));
  }

  log (operation: string) {
    console.log(operation);
  }

    /**
   * Handle Http operation that failed.
   * Let the app continue.
   * @param operation - name of the operation that failed
   * @param result - optional value to return as the observable result
   */
  private handleError<T> (operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      console.error(error); // log to console instead
      this.logger.debug(`${operation} failed: ${error.message}`);
      return of(result as T);
    };
  }
}

这是我的 games.component.ts:

import { Component, OnInit } from '@angular/core';
import { Game } from '../../models/game';
import { GameService } from 'src/app/services/game.service';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-games',
  templateUrl: './games.component.html',
  styleUrls: ['./games.component.css']
})
export class GamesComponent implements OnInit {
  games: Game[];

  constructor(private gameService: GameService) { 
  }

  ngOnInit() {
    this.getGames();
  }

  getGames(): void {
    this.gameService.getGames().subscribe(games => this.games = games);
    console.log(`games: ${this.games}`);
  }

  getGame(id: number): Observable<Game> {
    return this.gameService.getGame(id);
  }
}

如您所见,我正在 games.component.ts 中调用 getGames,其中 game.service.ts正在返回响应(Observable)。

由于某种原因 subscribe(games => this.games = games) 不起作用,我得到了 games 实例变量的“未定义”。 我肯定会得到正确的响应,因为 subscribe(games => console.log(games)) 不显示“未定义”,而是显示一个对象数组。

为什么没有分配我的实例变量?

编辑: 这是console.log中的输出,如果我订阅(games => console.log(games))

编辑: 如果我执行以下操作,则控制台日志是正确的。但是,如果我引用它之外的“游戏”数组,我会再次未定义:

  getGames(): void {
    this.gameService.getGames().subscribe((games) => {
      this.games = games;
      console.log(`games: ${this.games}`);
    });
    console.log(`all games: ${this.games}`); //this is undefined
  }

编辑:已解决 - 谢谢 dileepkumar jami 解决方案是删除 $ 符号和 '|我的模板中的“异步”:

<li *ngFor="let game of games$ | async">{{game.title}}</li>

<li *ngFor="let game of games">{{game.title}}</li>

【问题讨论】:

  • 您在 console.log 中打印什么?游戏或this.games
  • this.games 未定义。如果我订阅(games => console.log(games)) 我得到正确的数组
  • 你能给我们看看console.log(games)的输出吗
  • 是的。我刚刚用图片的链接编辑了这篇文章(我还不能嵌入图片,因为我是新用户)
  • 只是为了测试,尝试完全从您的服务中移除管道部件并检查。同时向我们展示游戏界面

标签: angular typescript angular-in-memory-web-api


【解决方案1】:
  1.    getGames(): void { 
  2.      this.gameService.getGames().subscribe((games) => {
  3.        this.games = games;
  4.        console.log(`games: ${this.games}`);
  5.      });
  6.      console.log(`all games: ${this.games}`); //this is undefined
  7. }

我给你的代码行编号了。

正如预期的那样,the line6 将返回 undefined

line 2 to line 5 的代码需要一些时间才能完成,因为它有一个 API 调用。

因为 Javascript 是异步的,它不会等待代码(从第 2 行到第 5 行)完成。 它开始执行line6。但是,到那时,this.gamesundefined

即使你在浏览器控制台中看到,你也会看到line6的输出首先然后你可以看到line4

您可以执行以下代码块来查看 javascript 是如何异步工作的

function myFunction() {
  console.log('Hello');
}

setTimeout(myFunction, 3000);
console.log('hi');
As you can see, even though console.log('hi'); was written after setTimeout(myFunction, 3000);, hi would be printed first and then hello.

<p> So, javascript did not wait for the setTimeout(myFunction, 3000); to be finished and it started to execute the next line
</p>

编辑:已解决 - 谢谢 dileepkumar jami 解决方案是删除 $ 符号和 '|我的模板中的“异步”:

<li *ngFor="let game of games$ | async">{{game.title}}</li>

<li *ngFor="let game of games">{{game.title}}</li>

【讨论】:

  • 谢谢你的帮助,我现在明白了。但我仍然不明白为什么我无法在我的模板中打印任何内容。我有
  • {{game.name}}
  • 但什么也没显示
  • 应该是"let game of games"。去掉美元符号。事实上,您不需要 async 管道,因为您正在调用订阅
  • 它通过删除美元符号和 '|异步'。但为什么?在下面的 Angular 教程中,它说要这样做(在 hero-search.component.html 页面的最底部)angular.io/tutorial/toh-pt6
  • 附加美元只是一个约定。请参阅教程中的组件。它仅使用 heros$ 初始化。此外,您不需要使用异步管道,因为在本教程中, heros$ 是一个返回数组的可观察对象,但在我们的例子中,游戏只是一个数组
  • 哇。我完全错过了。还有那个教程中的句子:'AsyncPipe 自动订阅 Observable,所以你不必在组件类中这样做'
  • 【解决方案2】:

    试试这个

    getGames(): void {
        this.gameService.getGames().subscribe(games => {
          this.games = games;
          console.log(`games: ${this.games}`);
        );   
    
    }
    

    【讨论】:

    • 奇怪。此订阅中的日志是正确的。但是如果我在它之外引用游戏数组,它又是未定义的。
    • 是的,仅按预期工作,控制台日志在您对服务器的调用完成之前打印。这是角度的异步行为。如果您真的想查看日志,请在延迟后打印(setTimeout 会帮助您)。
    【解决方案3】:

    您可以在订阅箭头函数中访问游戏数组,您正在尝试 console.log(games: ${this.games});在您的服务返回响应之前。

     this.gameService.getGames().subscribe((games) =>  {
       this.games = games;
       console.log(`games: ${this.games}`);
     });
    

    一旦分配了 games 属性,您就可以在组件中的任意位置访问该值。仍然无法访问该值,那么您应该检查控制台中是否有任何错误。

    更新 Javascript 是异步的,它不会等待您的服务完成来执行其他代码,在您的情况下,getGames 中的console.log() 在调用订阅之前首先执行。这样this.games 的值为undefined

    getGames(): void {
        this.gameService.getGames().subscribe((games) => {
          this.games = games;
          console.log(`games: ${this.games}`);
        });
        console.log(`all games: ${this.games}`); //this is undefined
      }
    

    【讨论】:

    • 这与其他答案类似。日志在您的回答中是正确的,但是如果我在它之外引用游戏数组,我会再次变得未定义。 this.gameService.getGames().subscribe((games) => { this.games = games; console.log(games: ${this.games}); });控制台.log(games: ${this.games}); // 未定义
    • 您的控制台是否还有其他错误?这可能会阻止角度变化检测。
    • Javascript 是异步的,不能console.log(games: ${this.games}) 旁边的订阅。在分配属性之前 console.log 首先执行这就是为什么记录的未定义
    • 我明白了,如果我在 ngOnInit() 中对“getGames()”的方法调用之后打印“游戏”数组,它应该在那里工作吗?
    • 不,它不会起作用,如果你想做任何事情,你应该在订阅函数中做。您可以创建一个单独的方法并在订阅中调用它。
    猜你喜欢
    相关资源
    最近更新 更多
    热门标签