【问题标题】:How to display one value of Observable<any[]> in Angular template until next click如何在 Angular 模板中显示 Observable<any[]> 的一个值,直到下次单击
【发布时间】:2018-02-12 05:48:36
【问题描述】:

目标:我有一个测验组件,我希望能够在我的模板中一次显示一个问题。用户可以在单击“下一步”按钮时显示下一个问题。

问题:我有一个 FirebaseListObservable,其中包含整个问题列表。如何一次只将一个渲染到我的模板。到目前为止,我粘贴了下面的代码。它呈现整个列表。我不知道从哪里开始,部分原因是我的 RxJS 知识初学者水平。

import { Component, OnInit } from '@angular/core';
import { FirebaseService } from '../../firebase.service';
import { Observable } from 'rxjs/Observable';
import { Question } from '../../model/question';

@Component({
  selector: 'app-quiz',
  template: `
    <md-grid-list cols="1" rowHeight="2:1">
      <md-grid-tile>
        <md-card *ngFor="let question of questions$ | async">
          <md-card-header>
            <md-card-title>{{question?.course}}</md-card-title>
            <md-card-subtitle>{{question?.chapter}}</md-card-subtitle>
          </md-card-header>
          <md-card-content>
            <p>{{question?.question}}</p>
          </md-card-content>
          <md-card-actions>
            <button md-button>See Answer</button>
            <button (click)="nextQuestion(question)" md-button>Next 
Question</button>            
          </md-card-actions>
        </md-card>
      </md-grid-tile>
    </md-grid-list> 
  `,
  styles: [`.example-card { width: 400px;}`]
})
export class QuizComponent implements OnInit {

  questions$: Observable<Question[]>;

  constructor(private fbDatabase: FirebaseService) { }

  ngOnInit() {
    this.questions$ = this.fbDatabase.getFirebaseList('/questions');
  }

  nextQuestion() {
  }

}

【问题讨论】:

  • 刚刚更新了我的答案。让我知道这是否有效

标签: angular firebase rxjs


【解决方案1】:

首先,我会利用组件模式创建一个组件来显示一个问题:

import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';

export interface IQuestion {
  label: string;
  answers: string[];
}

@Component({
  selector: 'app-question',
  template: `
    <div>
      <b>Question:</b>
      <p>{{ question.label }}</p>

      <b>Possible answers</b>
      <p *ngFor="let answer of question.answers">{{ answer }}</p>      
    </div>
  `,
  styles: [``],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppQuestionComponent {
  @Input() question: IQuestion;
}

那么,AppComponent的代码和它的cmets我觉得就够看懂了:
TS代码

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  question$: Observable<IQuestion>;
  questions$: Observable<IQuestion[]>;

  _click$ = new Subject<void>();
  click$ = this._click$.startWith(null);

  constructor(private firebaseService: FirebaseService) { }

  ngOnInit() {
    // get the questions from firebase
    this.questions$ = this
      .firebaseService
      .getFirebaseList('your-list')
      // add a false statement so we know when to ends
      .map(questions => [...questions, false]);

    const questionsOneByOne$ = this
      .questions$
      .mergeMap(
        questions =>
          questions.map(
            // explode each question to a different stream value...
            question => Observable.of(question)
          )
      )
      // ...so we can get them one by one
      .concatAll();

    this.question$ = Observable
      .zip(questionsOneByOne$, this.click$)
      .map(([question, _]) => question);
  }

  nextQuestion() {
    this._click$.next();
  }
}

HTML 代码

<div *ngIf="question$ | async as question; else noMoreQuestions">
  <app-question [question]="question"></app-question>

  <button (click)="nextQuestion()">Go to next question</button>
</div>

<ng-template #noMoreQuestions>
  No more questions
</ng-template>

这是 Stackblitz 上的现场演示(带有模拟的 firebase 列表)
https://stackblitz.com/edit/angular-mbnscx

如果您还有其他问题,请告诉我;)

【讨论】:

    【解决方案2】:

    如果您不想显示以前的问题,您应该一次只渲染问题数组的每个项目(问题对象)。 我会添加一个新的可观察变量类型 Question 来渲染到视图中。一个索引变量,用于跟踪数组中的项目。类似的东西:

    @Component({
          selector: 'app-quiz',
          template: `
            <md-grid-list cols="1" rowHeight="2:1">
              <md-grid-tile>
                <md-card *ngIf="question | async">
                  <md-card-header>
                    <md-card-title>{{(question | async)?.course}}</md-card-title>
                    <md-card-subtitle>{{(question | async)?.chapter}}</md-card-subtitle>
                  </md-card-header>
                  <md-card-content>
                    <p>{{(question | async)?.question}}</p>
                  </md-card-content>
                  <md-card-actions>
                    <button md-button>See Answer</button>
                    <button (click)="nextQuestion(index + 1)" md-button>Next 
        Question</button>            
                  </md-card-actions>
                </md-card>
              </md-grid-tile>
            </md-grid-list> 
          `,
          styles: [`.example-card { width: 400px;}`]
        })
    
        export class QuizComponent implements OnInit {
    
          questions: Array<Question>;
    
          question: Observable<Question>;
          index: number = 0;
    
          constructor(private fbDatabase: FirebaseService) { }
    
          ngOnInit() {
              this.fbDatabase.getFirebaseList('/questions').subscribe((res) =>{
                  if(res) {
                    console.log(res);
                    this.questions = res;
                    this.question = this.nextQuestion(index);
                  }
              });
    
          }
    
          nextQuestion(i: number): Observable<Question> {
              if i >= this.questions.length {
                i = 0;
              }
    
              return Observable.of(this.questions[i]);
          }
    
        }
    

    让我知道这是否适合你?

    【讨论】:

    • 我的模板显示为空白,但控制台中没有错误。我将 console.log(this.questions, this.question) 添加到 nextQuestion 函数的顶部,发现数组已初始化但 Observable 未定义,直到我第一次单击 Next。点击后,我的 md-card 还是空白。然而,控制台现在显示 Scalar Observable。如果我继续单击下一步,我会不断得到相同的 observable。我列表中的第一个。
    • @LandonWaldner 我刚刚更新了答案。我添加了ngIf,以确保在渲染之前我们从firebase返回数据,并在模板中更正async(如果有效,您可以从问题对象中删除*ngIf或async。另外,我添加了一个console.log 查看从 firebase 返回的数据。如果此解决方案不起作用,请将 console.log 输出发送给我,因为我不知道您的数据结构。
    • ngOnInit() { this.index = 0; this.fbDatabase.getFirebaseList('/questions').subscribe( (res) => { if (res) { this.questions = res; this.nextQuestion(this.index); } } ); } nextQuestion(i: number) { if (i >= this.questions.length) { this.index = i = 0; } this.question = Observable.of(this.questions[i]);这个.index++; }
    • 糟糕。通过稍微调整上面的代码,我能够让它工作。我感谢您的所有帮助。尤其是转换为常规 Observable 的想法。我尝试投票,但我的声望不到 15,所以不算数。
    • 很高兴听到它有效。当你知道如何调整它时会更好。如果您认为我的回答有帮助,请将其标记为正确答案。如果您为自己的调整感到自豪,只需发布​​您自己的答案,以便其他人可以学习。
    猜你喜欢
    • 1970-01-01
    • 2018-08-25
    • 2018-03-18
    • 2018-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 2021-06-25
    相关资源
    最近更新 更多