【问题标题】:Async function not working as intended when called at onInit lifecycle hook在 onInit 生命周期挂钩处调用时,异步函数无法按预期工作
【发布时间】:2017-09-13 05:25:09
【问题描述】:

在我的项目中,我有一个服务应该在应用启动时加载本地数据库。为此,使用了函数GetData()。我尝试通过调用ngOnInit() 生命周期挂钩来使用它。它将结果记录在控制台中,但属性 datum 似乎没有改变。

但是,如果我将GetData() 方法添加到按钮,则属性会更改并且数据会按预期显示在控制台中。

我查看的所有资料都表明,如果我需要立即加载数据库,正确的方法是使用 ngOnInit() 钩子,这就是为什么当来自 DOM 的某些事件时我不想调用 GetData()被解雇了。

组件

export class AppComponent implements OnInit {
  datum;
  constructor(private searchService: SearchService){ }
  getData(){
    this.searchService.getData().subscribe(
      data => {
        this.datum = data[0];
        console.log(this.datum);},
      err => console.log("E", err)
    );
  }

  ngOnInit(){
    this.getData();
  }
  title = 'app';
}

HTML

<div style="text-align:center">
  <h1>
    Welcome to {{title}}!!
    Data: {{datum | json}}
  </h1>
  <button (click)="getData()">Click Me</button>

应用已加载

点击按钮

编辑 按照 AlexKhymenko 的建议,使用 setTimeout() 实现 getData()

  getData(){
    this.searchService.getData().subscribe(
      data => {
        setTimeout(() =>{
          this.datum = data[0];
        });
        console.log(this.datum);},
      err => console.log("E", err)
    );
  }

【问题讨论】:

  • 我会在这种情况下使用吸气剂:get Datum() { return this.datum; };,在视图中:Data: {{Datum}}。数据不显示可能有多种原因:zone.js 未触发,或者其他原因。您也可以尝试使用Zone.run,不过它应该已经用getter 修复了。此外,始终初始化类属性,如果理论上需要在视图中显示,请不要将其保持未定义。 NgZone 参考:angular.io/api/core/NgZone。或者,直接绑定到 dom 中的 getData,并返回 observable,然后添加 |异步
  • 我不太明白。 “getter”的目的和实现是什么?它只是一个返回datum的函数吗?什么时候调用?
  • @briosheje 也许直接绑定到getData() 是解决方案,因为我使用datum 只是因为我没有完全掌握可观察对象的概念。我该怎么做?它应该看起来像 {{ searchService.getData() 吗? |异步}}?
  • @briosheje 尝试使用 getter。似乎无所作为。我做对了吗?我在 HTML 中添加了get Datum() {return this.datum;} 并绑定到Datum 而不是datum
  • getter 应该不断更新视图,除非zone.js 有问题。在这种情况下,您需要运行包装在 zone.run 中的代码(在线查看)。否则,您可以通过引用 observable 来使用 async 管道。无论如何它应该在没有异步的情况下工作,并且应该已经工作,你应该真正检查你的代码在其他地方是否有其他错误,无论是在视图中,还是在其他地方;)

标签: javascript angular observable


【解决方案1】:

您需要做的是手动运行更改检测,例如通过调用 setTimeout。我创建了a Plunker

export class AppComponent implements OnInit {
  datum;
  title = 'app';

  constructor(private searchService: SearchService){ }
       }

 ngOnInit(){
   this.getData();
 }
  getData(){
      this.searchService.getData().subscribe(
  data => {
   setTimeout(() => {
            this.datum = data[0];
   })
    console.log(this.datum);},
  err => console.log("E", err)
);

}

【讨论】:

  • 要么我做错了什么,要么这没有帮助。在实施您的建议后,我在控制台中得到的第一个结果是undefined,并且 DOM 中没有显示任何内容。单击按钮后,控制台中的结果 logget 是正确的,并且信息出现在 DOM 中
  • 给我 5 分钟将创建 plunker
  • @IvanBespalov 已更新。它将在 2 秒后加载日期。
  • 即使没有 setTimeout 也能正常工作。但如果没有,您可以添加 setTimeout
  • 我试过你的方法。开始认为我使用 observables 的想法在某种程度上是该死的。我看到您在使用 Promise 的 plunker 上的示例工作正常,但是当我尝试将 setTimeout 添加到我的代码中时,它什么也不做,无论它立即触发还是延迟 2 秒后触发
【解决方案2】:

因为在订阅函数中你得到一个可观察的对象(不是数组)。

export class AppComponent implements OnInit {
   datum;
   constructor(private searchService: SearchService){ }
   getData(){
this.searchService.getData().subscribe(
  data => {
    //remove brackets
    this.datum = data;
    console.log(this.datum);},
  err => console.log("E", err)
 );
}

ngOnInit(){
   this.getData();
}
title = 'app';
}

【讨论】:

  • 删除括号只会改变我分配给datum的内容。如果我离开括号,则对象数组与单个对象相对。
【解决方案3】:

在 HTML 中尝试

<h1 *ngIf="datum !== null && datum !== undefined && datum !== ''">
Welcome to {{title}}!!
Data: {{datum | json}}
</h1>

您的数据变量是双向数据绑定。当数据不等于 null 或 undefined 或空白时,显示该 HTML 标记。

希望对您有所帮助。

【讨论】:

  • 变化不大。启动时只显示按钮。只有在单击按钮时才会出现其他所有内容。
  • 仍然是相同的行为,除了在应用程序启动时仅显示“单击我”按钮
  • 尝试调用 this.getData();构造函数中的方法,因为角度生命周期首先调用构造函数。
猜你喜欢
  • 2020-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-06
  • 2017-04-17
  • 2017-12-25
  • 2016-12-07
  • 2022-01-24
相关资源
最近更新 更多