如何在 Angular 中显示 Observable-Array 的一个 Observable?

How to display one Observable of an Observable-Array in Angular?

我有一个包含问题列表的 Observable-Array。我想一个一个地展示我的问题,但找不到办法。

到目前为止,我只在 html.

中用 *ngFor 显示了所有这些

这是我的组件:

import { Component, OnInit } from '@angular/core';
import { mergeMap, Observable, of, concatAll, Subject, startWith, zip, Subscription } from "rxjs";
import { Question } from "../models/Question";
import { QuestionService } from "../services/question.service";
import { AuthService } from "../services/auth.service";
import { User } from "../models/User";

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

  user_id: Pick<User, "id"> | undefined
  unanswered_questions$: Observable<Question[]> | undefined
  question$: Observable<Question> | undefined

  constructor(
    private questionService: QuestionService,
    private authService: AuthService
  ) { }

  ngOnInit(): void {
    this.user_id = this.authService.userId
    this.unanswered_questions$ = this.getUnansweredQuestions(this.user_id)
  }

  getUnansweredQuestions(user_id: Pick<User, "id"> | undefined):    Observable<Question[]> {
    return this.questionService.fetchAllUnansweredQuestions(user_id);
  }
}

这是我的 html:

<mat-card class="question-card" *ngFor="let question of unanswered_questions$ | async">
  <mat-card-header>
    <div mat-card-avatar class="example-header-image"></div>
    <mat-card-title>{{question.title}}</mat-card-title>
  </mat-card-header>
  <mat-card-content>
    <h3>{{question.body}}</h3>
  </mat-card-content>
  <mat-card-actions>
    <button mat-button>{{question.answer1}}</button>
    <button mat-button>{{question.answer2}}</button>
    <button mat-raised-button color="accent">Skip<mat-icon>skip_next</mat-icon></button>
  </mat-card-actions>
</mat-card>

我发现 this Post 有人在尝试做基本相同的事情。不幸的是,post 的两个答案都不适合我。我认为它们不起作用,因为 post 已经 4 岁了,而我使用的是更新版本的 rxjs 和 angular.

非常感谢任何帮助。谢谢!

您可以使用不同的解决方法来做到这一点。只要定义一个变量currentQuestionNumber =1;

然后检查这个变量是否匹配HTML

中的index+1
         <mat-card
            class="question-card"
            *ngFor="let question of unanswered_questions$ | async; let i = index"
          >
            <ng-container *ngIf="currentQuestionNumber === i + 1">
              <mat-card class="question-card">
                <mat-card-header>
                  <div mat-card-avatar class="example-header-image"></div>
                  <mat-card-title>{{ question.title }}</mat-card-title>
                </mat-card-header>
                <mat-card-content>
                  <h3>{{ question.body }}</h3>
                </mat-card-content>
                <mat-card-actions>
                  <button mat-button>{{ question.answer1 }}</button>
                  <button mat-button>{{ question.answer2 }}</button>
                  <button mat-raised-button color="accent">
                    Skip<mat-icon>skip_next</mat-icon>
                  </button>
                </mat-card-actions>
              </mat-card>
            </ng-container>
          </mat-card>

然后在“跳过”和“下一步”按钮上添加点击处理程序,以在每次点击时递增或递减 currentQuestionNumber

目前你的代码“有效”,因为你正在将你的 observable 传递给异步管道处理程序,该处理程序会一直执行 ngFor,直到 observable 被解析。

我会更改您的代码,以便您订阅 observable 并处理随后的结果。实际上,我会先转换为承诺并等待它,因为在我看来,这种风格更具可读性和可预测性,尤其是对于“一劳永逸”的事件。

所以像这样更改您的组件(注意:为了简洁起见,我省略了导入和装饰器):

export class PlayComponent implements OnInit {

  user_id: Pick<User, "id"> | undefined;
  unanswered_questions: Question[];
  question$: Observable<Question> | undefined;
  questionIndex = 0;

  constructor(
    private questionService: QuestionService,
    private authService: AuthService
  ) { }

  async ngOnInit(): Promise<void> {
    this.user_id = this.authService.userId;
    await this.unanswered_questions = this.getUnansweredQuestions(this.user_id).toPromise();
  }

  getUnansweredQuestions(user_id: Pick<User, "id"> | undefined):    Observable<Question[]> {
    return this.questionService.fetchAllUnansweredQuestions(user_id);
  }

  skipQuestion(): void {
    if (this.questionIndex !== this.getUnansweredQuestions.length) {
     this.questionIndex++;
     }
  }
}

然后是你的 HTML:

<mat-card class="question-card" *ngIf="unanswered_questions">
  <mat-card-header>
    <div mat-card-avatar class="example-header-image"></div>
    <mat-card-title>{{unanswered_questions[questionIndex].title}}</mat-card-title>
  </mat-card-header>
  <mat-card-content>
    <h3>{{unanswered_questions[questionIndex].body}}</h3>
  </mat-card-content>
  <mat-card-actions>
    <button mat-button>{{unanswered_questions[questionIndex].answer1}}</button>
    <button mat-button>{{unanswered_questions[questionIndex].answer2}}</button>
    <button mat-raised-button color="accent" (click)="skipQuestion()">Skip<mat-icon>skip_next</mat-icon></button>
  </mat-card-actions>
</mat-card>

通过将 ngIf 添加到您的 matCard 中,您可以在解析 observable 之前阻止渲染。