diff --git a/src/addons/mod/quiz/pages/player/player.page.ts b/src/addons/mod/quiz/pages/player/player.page.ts index ceaf636da..826fe4d67 100644 --- a/src/addons/mod/quiz/pages/player/player.page.ts +++ b/src/addons/mod/quiz/pages/player/player.page.ts @@ -47,6 +47,7 @@ import { CanLeave } from '@guards/can-leave'; import { CoreForms } from '@singletons/form'; import { CoreDom } from '@singletons/dom'; import { CoreTime } from '@singletons/time'; +import { CoreComponentsRegistry } from '@singletons/components-registry'; /** * Page that allows attempting a quiz. @@ -264,7 +265,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { return; } else if (page == this.attempt.currentpage && !this.showSummary && slot !== undefined) { // Navigating to a question in the current page. - this.scrollToQuestion(slot); + await this.scrollToQuestion(slot); return; } else if ((page == this.attempt.currentpage && !this.showSummary) || (fromModal && this.isSequential && page != -1)) { @@ -320,7 +321,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { if (slot !== undefined) { // Scroll to the question. - this.scrollToQuestion(slot); + await this.scrollToQuestion(slot); } } } @@ -687,8 +688,10 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { * * @param slot Slot of the question to scroll to. */ - protected scrollToQuestion(slot: number): void { - CoreDom.scrollToElement( + protected async scrollToQuestion(slot: number): Promise { + await CoreUtils.nextTick(); + await CoreComponentsRegistry.waitComponentsReady(this.elementRef.nativeElement, 'core-question'); + await CoreDom.scrollToElement( this.elementRef.nativeElement, '#addon-mod_quiz-question-' + slot, ); diff --git a/src/core/features/question/components/question/question.ts b/src/core/features/question/components/question/question.ts index 4d17d9ef4..7def0c42a 100644 --- a/src/core/features/question/components/question/question.ts +++ b/src/core/features/question/components/question/question.ts @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, Output, OnInit, EventEmitter, ChangeDetectorRef, Type } from '@angular/core'; +import { Component, Input, Output, OnInit, EventEmitter, ChangeDetectorRef, Type, ElementRef } from '@angular/core'; +import { AsyncComponent } from '@classes/async-component'; +import { CorePromisedValue } from '@classes/promised-value'; import { CoreQuestionBehaviourDelegate } from '@features/question/services/behaviour-delegate'; import { CoreQuestionDelegate } from '@features/question/services/question-delegate'; @@ -20,6 +22,7 @@ import { CoreQuestionBehaviourButton, CoreQuestionHelper, CoreQuestionQuestion } import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; import { Translate } from '@singletons'; +import { CoreComponentsRegistry } from '@singletons/components-registry'; import { CoreLogger } from '@singletons/logger'; /** @@ -30,7 +33,7 @@ import { CoreLogger } from '@singletons/logger'; templateUrl: 'core-question.html', styleUrls: ['../../question.scss'], }) -export class CoreQuestionComponent implements OnInit { +export class CoreQuestionComponent implements OnInit, AsyncComponent { @Input() question?: CoreQuestionQuestion; // The question to render. @Input() component?: string; // The component the question belongs to. @@ -50,15 +53,24 @@ export class CoreQuestionComponent implements OnInit { data: Record = {}; // Data to pass to the component. seqCheck?: { name: string; value: string }; // Sequenche check name and value (if any). behaviourComponents?: Type[] = []; // Components to render the question behaviour. - loaded = false; + promisedReady: CorePromisedValue; + validationError?: string; protected logger: CoreLogger; - constructor( - protected changeDetector: ChangeDetectorRef, - ) { + get loaded(): boolean { + return this.promisedReady.isResolved(); + } + + constructor(protected changeDetector: ChangeDetectorRef, private element: ElementRef) { this.logger = CoreLogger.getInstance('CoreQuestionComponent'); + this.promisedReady = new CorePromisedValue(); + CoreComponentsRegistry.register(this.element.nativeElement, this); + } + + async ready(): Promise { + await this.promisedReady; } /** @@ -69,7 +81,7 @@ export class CoreQuestionComponent implements OnInit { if (!this.question || (this.question.type != 'random' && !CoreQuestionDelegate.isQuestionSupported(this.question.type))) { - this.loaded = true; + this.promisedReady.resolve(); return; } @@ -80,7 +92,7 @@ export class CoreQuestionComponent implements OnInit { ); if (!this.componentClass) { - this.loaded = true; + this.promisedReady.resolve(); return; } @@ -163,7 +175,7 @@ export class CoreQuestionComponent implements OnInit { ); } finally { this.question.html = CoreDomUtils.removeElementFromHtml(this.question.html, '.im-controls'); - this.loaded = true; + this.promisedReady.resolve(); } }