简体   繁体   中英

Ionic2 changedetection issue, first change ignored

I'm developing a quiz app using ionic2. The questions are pulled from a database and displayed using the <ion-slides> -component. The important parts of the template look like the following:

<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>

The component code looks like the following:

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)
        );
    }
    ...
}

First the questions array is filled only with QuizTypes such that the slides can be created. Then I load question data ( QuizDefinition ) for the first two questions from the database. When advancing to the next question I load the data for the question after that.

On my PC this works as expected, but on the mobile the first question is not updated when the view is rendered or the change is just not recognized. In any way the JSONPipe still shows the question type QuizType instead of the question data. Any slide after the first one works correctly.

EDIT

On my PC I use a Mockup for the database. Which did not have a delay. I added a random delay and now it behaves the same as on mobile. The first question data is not set.

I solved it now with switching to completely manual control of change detection and only run it when changes really are expected to happen.

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();
        });
    }
    ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM