【问题标题】:*ngFor is updating before it gets the new data*ngFor 在获取新数据之前正在更新
【发布时间】:2020-02-15 04:18:44
【问题描述】:

我目前正在制作一个“会议列表”,用户可以在其中创建新会议并加入其他会议。

我的堆栈如下所示:

  • 前端:角度
  • API:Firebase 云函数
  • 数据库:Firebase 实时数据库
    对于会议列表,我使用此标记,因为它应该呈现每个会议及其名称的列表。
<div class="container">
  <h2>Aktuelle Meetings</h2>
  <div class="cardContainer" *ngFor="let meeting of meetings">
    <div class="meeting">
      <div class="meetingText">
        <p class="titleText">{{ meeting[1].title }}</p>
      </div>
    </div>
  </div>
  <div class="createMeeting">
    <button class="floating" mat-fab (click)="openCreateMeeting()">
      <mat-icon>add</mat-icon>
    </button>
  </div>
</div>

openCreateMeeting() 正在打开一个 MaterialDialog (Angular Material),用户可以在其中输入有关会议的一些基本信息。

  openCreateMeeting(): void {
    const dialogConfig = new MatDialogConfig();
    dialogConfig.autoFocus = true;
    const dialogRef = this.dialog.open(NewMeetingComponent, dialogConfig);

    dialogRef.afterClosed().subscribe((data) => {
      this.getMeetings();
    });
  }

getMeeting() 发送 API 调用以从数据库中获取会议。


当用户创建新事件时,它会触发我的“apiService”,其中包括创建新会议的功能。
 public postMeeting(
    eventTitle,
    eventDescription,
    eventLocation,
    eventDate,
    eventOwner
  ): void {
    console.log('creating an event ...');
    const data = {
      title: eventTitle,
      description: eventDescription,
      location: eventLocation,
      date: eventDate,
      owner: eventOwner,
      participants: eventOwner,
    };
    this.httpClient.post(this.apiUrl, data).subscribe((data) => {
      console.log('Event created');
    });
  }

但是,当我创建新事件时,新事件不会显示在列表中。它会在新项目从 API/DB 返回之前刷新。因此,仅当我刷新页面或创建下一个项目时才会显示新事件。

我现在要做的是以某种方式等待 API 调用完成,然后再刷新我的 *ngFor

问题:那么在重新渲染 *ngFor 之前,我将如何等待数据出现?


我尝试了什么:

我尝试包含changeDetector.markForCheck()

dialogRef.afterClosed().subscribe((data) => {
    this.getMeetings();
    this.changeDetector.markForCheck();
});

将 changeDetector 直接包含在我的getMeetings() 中。

public getMeetings() {
    const meetings = this.httpClient.get(this.apiUrl);
    this.changeDetector.markForCheck();
    return meetings;
}

这并没有解决问题,但我的应用程序崩溃并给了我这个错误:

  StaticInjectorError(Platform: core)[MeetingListComponent -> ChangeDetectorRef]: 
    NullInjectorError: No provider for ChangeDetectorRef!
NullInjectorError: StaticInjectorError(AppModule)[MeetingListComponent -> ChangeDetectorRef]: 
  StaticInjectorError(Platform: core)[MeetingListComponent -> ChangeDetectorRef]: 
    NullInjectorError: No provider for ChangeDetectorRef!
    at NullInjector.get (core.js:778)
    at resolveToken (core.js:2564)
    at tryResolveToken (core.js:2490)
    at StaticInjector.get (core.js:2353)
    at resolveToken (core.js:2564)
    at tryResolveToken (core.js:2490)
    at StaticInjector.get (core.js:2353)
    at resolveNgModuleDep (core.js:26403)
    at NgModuleRef_.get (core.js:27491)
    at injectInjectorOnly (core.js:657)
    at resolvePromise (zone-evergreen.js:797)
    at resolvePromise (zone-evergreen.js:754)
    at zone-evergreen.js:858
    at ZoneDelegate.invokeTask (zone-evergreen.js:391)
    at Object.onInvokeTask (core.js:34182)
    at ZoneDelegate.invokeTask (zone-evergreen.js:390)
    at Zone.runTask (zone-evergreen.js:168)
    at drainMicroTaskQueue (zone-evergreen.js:559)
    at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:469)
    at invokeTask (zone-evergreen.js:1603)

这没有做任何事情,因为 angular 可能正在接受检查,但新数据还没有。

【问题讨论】:

    标签: javascript html angular typescript asynchronous


    【解决方案1】:

    在你的构造函数中添加private cdr: ChangeDetectorRef,然后在你的getMeetings函数中添加this.cdr.markForCheck();,在this.meetings被填充之后。

    示例:

    getMeetings() {
            ....
            ....
            // response API service
            this.meetings = response;
            this.cdr.markForCheck();
        }
    

    【讨论】:

    • 我之前尝试过这个(忘记在帖子中包含它,但现在这样做了)。它没有解决问题(帖子中的原因)
    • 在填充会议后,您必须在 getMeetings() 函数中添加 this.cdr.markForCheck();
    • 和以前一样的问题。但这一次它带有一个巨大的错误消息(因为它似乎不想将更改检测器添加到构造函数中)。
    【解决方案2】:

    我会在发布请求回调中添加关闭模式函数,这样模式在会议保存到数据库之前不会关闭。像这样的。

    this.httpClient.post(this.apiUrl, data).subscribe((data) => {
      console.log('Event created');
      this.dialogRef.close()
    });
    

    更好的选择是查看 rxjs 主题。

    【讨论】:

    • post 请求由服务处理。但我可以尝试某种回调函数。这可以在 POST 完成后立即关闭模式。
    • 不要订阅该服务。只需返回 this.httpClient.post(this.apiUrl, data)。然后从你的模态组件订阅。
    【解决方案3】:

    点击提交时直接在列表中显示此会议并为其提供saving 状态,然后等待数据库保存。

    【讨论】:

      【解决方案4】:

      嗯,您确定您的 API 在您调用 GET 请求之前完成了 POST 请求吗?我会建议 2 件事中的 1 件事:

      • 在您的 POST 方法响应后立即调用您的 this.getMeetings();,而不是在您关闭对话框时。
      • 如果您要添加一个新会议,为什么不简单地将该会议添加到会议列表中呢?您可以在 POST 响应之后使用服务器中的数据执行此操作,也可以直接使用表单中插入的值执行此操作

      【讨论】:

      • 关于你的第二个想法。所以目前我正在获取整个会议列表。相反,我应该尝试获取最新的会议,然后将其附加到我当前的列表中?这样做是因为我认为它可能更容易(似乎我错了)
      • @Ironlors 我在第二个想法中的意思是,理想情况下,在 Restfull API 中,您希望返回在 Http Post 方法上创建的 201,该方法添加返回您添加的会议的记录,无需获取最后会议。
      • 然后我可以从响应中获取这些数据并将其添加到我的列表中吗?好吧,我要返回会议,但没有考虑过以这种方式使用它:O。目前,它只是为了调试而存在。
      • 是的,您可以避免 1 个列表请求,这是一种更好的方法,特别是如果您打算针对移动用户来避免经常传输大数据。
      猜你喜欢
      • 1970-01-01
      • 2018-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多