我使用ionic2开发了一个测验应用。这些问题是从数据库中提取的,并使用<ion-slides>
-Component显示。模板的重要部分看起来如下:
<ion-content padding>
...
<ion-slides *ngIf="status === QuizState.running" pager="true" paginationType="progress">
<ion-slide *ngFor="let question of questions; let index = index">
<span>{{ question|json}}</span>
...
</ion-slide>
</ion-slides>
...
</ion-content>
组件代码看起来如下:
import { Component, ViewChild, ChangeDetectionStrategy } from '@angular/core';
import { NavController, Slides } from 'ionic-angular';
import { QuizLoaderService, QuizType, QuizDefinition } from '../../components/quiz-item/quiz-loader.service';
import { SamplePipe } from 'ngx-pipes/src/app/pipes/array/sample';
import { ShufflePipe } from 'ngx-pipes/src/app/pipes/array/shuffle';
import { Observable } from 'rxjs';
import 'rxjs/add/observable/from';
import 'rxjs/add/observable/forkJoin';
enum QuizState {
idle,
running,
finished
}
@Component({
selector: 'page-quiz',
templateUrl: 'quiz.html',
providers: [
SamplePipe, ShufflePipe, QuizLoaderService
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class QuizPage {
private slides: Slides;
private status: QuizState = QuizState.idle;
private questions: Array<QuizType|QuizDefinition> = [];
private readonly numberOfQuestions = 10;
// make enum accessable from the view
readonly QuizState = QuizState;
constructor(public navCtrl: NavController, private quizLoaderService: QuizLoaderService) {}
@ViewChild(Slides) set slidesViewChild(slides: Slides) {
if(slides) {
slides.onlyExternal = true;
slides.ionSlideWillChange.subscribe(slides => {
// load the after next question in advance
if(!slides.isEnd()) {
let index: number = slides.getActiveIndex() + 1;
let quizType: QuizType = <QuizType>this.questions[index];
this.quizLoaderService.loadQuizItem(quizType).subscribe((item: QuizDefinition) => this.questions[index] = item);
}
});
}
this.slides = slides;
}
startQuiz() {
this.status = QuizState.running;
// generate an array of question types
this.questions = this.quizLoaderService.getQuizItems(this.numberOfQuestions);
// fetch data for the first two questions
let startingQuestions: Observable<QuizDefinition>[] = [0, 1].map(index => {
let quizType = <QuizType>this.questions[index];
return this.quizLoaderService.loadQuizItem(quizType);
});
Observable.forkJoin(startingQuestions).subscribe(
// update first two items with actual question data
(items: QuizDefinition[]) => this.questions.splice(0, items.length, ...items)
);
}
...
}
首先,questions
数组仅被QuizTypes
填充,因此可以创建幻灯片。然后,我为数据库中的前两个问题加载问题数据(QuizDefinition
(。在进入下一个问题时,我在此之后为问题加载数据。
在我的PC上可以按预期工作,但是在移动设备上,当视图渲染或更改只是无法识别时,第一个问题不会更新。以任何方式,JSONPipe
仍然显示问题类型QuizType
而不是问题数据。第一个幻灯片在第一个幻灯片正常工作后。
编辑
在我的PC上,我为数据库使用模型。没有延迟。我添加了一个随机延迟,现在的行为与移动设备相同。第一个问题数据未设置。
我现在解决了它,通过切换到完全手动控制变更检测,并且只有在预期发生更改时才能运行它。
import { Component, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { NavController, Slides } from 'ionic-angular';
import { QuizLoaderService, QuizType, QuizDefinition } from '../../components/quiz-item/quiz-loader.service';
import { SamplePipe } from 'ngx-pipes/src/app/pipes/array/sample';
import { ShufflePipe } from 'ngx-pipes/src/app/pipes/array/shuffle';
import { Observable } from 'rxjs';
import 'rxjs/add/observable/forkJoin';
enum QuizState {
idle,
running,
finished
}
@Component({
selector: 'page-quiz',
templateUrl: 'quiz.html',
providers: [
SamplePipe, ShufflePipe, QuizLoaderService
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class QuizPage {
private slides: Slides;
private status: QuizState = QuizState.idle;
private questions: Array<QuizType|QuizDefinition> = [];
private readonly numberOfQuestions = 10;
// make enum accessable from the view
readonly QuizState = QuizState;
constructor(public navCtrl: NavController, private quizLoaderService: QuizLoaderService, private changeDetectorRef: ChangeDetectorRef) {}
ngAfterViewInit() {
// disable change detection
this.changeDetectorRef.detach();
}
...
startQuiz() {
this.status = QuizState.running;
this.questions = this.quizLoaderService.getQuizItems(this.numberOfQuestions);
//this.changeDetectorRef.detectChanges();
// fetch data for the first two questions
let startingQuestions: Observable<QuizDefinition>[] = [0, 1].map(index => {
let quizType = <QuizType>this.questions[index];
return this.quizLoaderService.loadQuizItem(quizType);
});
Observable.forkJoin(startingQuestions).subscribe((items: QuizDefinition[]) => {
this.questions.splice(0, items.length, ...items);
// run change detection when changes happen
this.changeDetectorRef.detectChanges();
});
}
...
}