Tengo un Observable-Array que contiene una lista de preguntas. Me gustaría mostrar mis preguntas una por una, pero no encuentro la manera de hacerlo.
Hasta ahora solo logré mostrarlos todos con un * ngFor en mi html.
Este es mi componente:
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); } }Este es mi 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>Encontré esta publicación donde alguien está tratando de hacer básicamente lo mismo. Desafortunadamente, ambas respuestas en esa publicación no funcionan para mí. Pensé que no funcionan debido a que la publicación tiene 4 años y yo uso una versión más nueva de rxjs y angular.
Cualquier ayuda es muy apreciada. ¡Gracias!
Actualmente, el código "funciona" porque está canalizando su observable al controlador de canalización asíncrona que mantiene la ejecución de ngFor hasta que se resuelva el observable.
Modificaría su código para que se suscriba a lo observable y maneje el resultado resultante. En realidad, primero convertiría a una promesa y la esperaría ya que, en mi opinión, ese estilo es más legible y predecible, especialmente para un evento "uno y listo".
Así que modifique su componente así (NOTA: he dejado fuera las importaciones y el decorador por razones de brevedad):
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++; } } }Y luego tu 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>Al agregar ngIf a su matCard, evita la representación hasta que se resuelva el observable.