MOBILE-2577 quiz: Fix position of drags when question contains images

main
dpalou 2018-09-13 17:19:07 +02:00
parent ab1c124713
commit c14d7ad04a
7 changed files with 62 additions and 46 deletions

View File

@ -7,7 +7,7 @@
<ion-icon name="information-circle"></ion-icon>
{{ 'core.question.howtodraganddrop' | translate }}
</p>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text" #questiontext></core-format-text></p>
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId" [text]="question.ddArea" (afterRender)="questionRendered()"></core-format-text>
</ion-item>
</section>

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, OnInit, OnDestroy, Injector, ElementRef } from '@angular/core';
import { Component, OnInit, OnDestroy, Injector, ElementRef, ViewChild } from '@angular/core';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component';
import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker';
@ -25,6 +25,7 @@ import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker';
templateUrl: 'addon-qtype-ddmarker.html'
})
export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent implements OnInit, OnDestroy {
@ViewChild('questiontext') questionTextEl: ElementRef;
protected element: HTMLElement;
protected questionInstance: AddonQtypeDdMarkerQuestion;
@ -87,9 +88,11 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple
*/
questionRendered(): void {
if (!this.destroyed) {
// Create the instance.
this.questionInstance = new AddonQtypeDdMarkerQuestion(this.loggerProvider, this.domUtils, this.textUtils, this.element,
this.question, this.question.readOnly, this.dropZones);
this.domUtils.waitForImages(this.questionTextEl.nativeElement).then(() => {
// Create the instance.
this.questionInstance = new AddonQtypeDdMarkerQuestion(this.loggerProvider, this.domUtils, this.textUtils,
this.element, this.question, this.question.readOnly, this.dropZones);
});
}
}

View File

@ -1,10 +1,13 @@
<section ion-list *ngIf="question.text || question.text === ''">
<ion-item text-wrap class="addon-qtype-ddwtos-container">
<!-- Content is outside the core-loading to let the script calculate drag items position -->
<core-loading [hideUntil]="question.loaded"></core-loading>
<ion-item text-wrap class="addon-qtype-ddwtos-container" [ngClass]="{invisible: !question.loaded}">
<p *ngIf="!question.readOnly" class="core-info-card" icon-start>
<ion-icon name="information-circle"></ion-icon>
{{ 'core.question.howtodraganddrop' | translate }}
</p>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text" #questiontext></core-format-text></p>
<core-format-text *ngIf="question.answers" [component]="component" [componentId]="componentId" [text]="question.answers" (afterRender)="questionRendered()"></core-format-text>
<div class="drags"></div>
</ion-item>

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, OnInit, OnDestroy, Injector, ElementRef } from '@angular/core';
import { Component, OnInit, OnDestroy, Injector, ElementRef, ViewChild } from '@angular/core';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component';
import { AddonQtypeDdwtosQuestion } from '../classes/ddwtos';
@ -25,6 +25,7 @@ import { AddonQtypeDdwtosQuestion } from '../classes/ddwtos';
templateUrl: 'addon-qtype-ddwtos.html'
})
export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent implements OnInit, OnDestroy {
@ViewChild('questiontext') questionTextEl: ElementRef;
protected element: HTMLElement;
protected questionInstance: AddonQtypeDdwtosQuestion;
@ -80,6 +81,8 @@ export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent impleme
this.question.text += inputEl.outerHTML;
this.inputIds.push(inputEl.getAttribute('id'));
});
this.question.loaded = false;
}
/**
@ -87,11 +90,15 @@ export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent impleme
*/
questionRendered(): void {
if (!this.destroyed) {
// Create the instance.
this.questionInstance = new AddonQtypeDdwtosQuestion(this.loggerProvider, this.domUtils, this.element, this.question,
this.question.readOnly, this.inputIds);
this.domUtils.waitForImages(this.questionTextEl.nativeElement).then(() => {
// Create the instance.
this.questionInstance = new AddonQtypeDdwtosQuestion(this.loggerProvider, this.domUtils, this.element,
this.question, this.question.readOnly, this.inputIds);
this.questionHelper.treatCorrectnessIconsClicks(this.element, this.component, this.componentId);
this.questionHelper.treatCorrectnessIconsClicks(this.element, this.component, this.componentId);
this.question.loaded = true;
});
}
}

View File

@ -16,6 +16,7 @@ import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core';
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module';
import { AddonQtypeDdwtosHandler } from './providers/handler';
import { AddonQtypeDdwtosComponent } from './component/ddwtos';
@ -27,6 +28,7 @@ import { AddonQtypeDdwtosComponent } from './component/ddwtos';
imports: [
IonicModule,
TranslateModule.forChild(),
CoreComponentsModule,
CoreDirectivesModule
],
providers: [

View File

@ -296,7 +296,7 @@ export class CoreFormatTextDirective implements OnChanges {
this.calculateHeight();
// Wait for images to load and calculate the height again if needed.
this.waitForImages().then((hasImgToLoad) => {
this.domUtils.waitForImages(this.element).then((hasImgToLoad) => {
if (hasImgToLoad) {
this.calculateHeight();
}
@ -628,39 +628,6 @@ export class CoreFormatTextDirective implements OnChanges {
this.iframeUtils.treatFrame(iframe);
}
/**
* Wait for images to load.
*
* @return {Promise<boolean>} Promise resolved with a boolean: whether there was any image to load.
*/
protected waitForImages(): Promise<boolean> {
const imgs = Array.from(this.element.querySelectorAll('img')),
promises = [];
let hasImgToLoad = false;
imgs.forEach((img) => {
if (img && !img.complete) {
hasImgToLoad = true;
// Wait for image to load or fail.
promises.push(new Promise((resolve, reject): void => {
const imgLoaded = (): void => {
resolve();
img.removeEventListener('loaded', imgLoaded);
img.removeEventListener('error', imgLoaded);
};
img.addEventListener('load', imgLoaded);
img.addEventListener('error', imgLoaded);
}));
}
});
return Promise.all(promises).then(() => {
return hasImgToLoad;
});
}
/**
* Convenience function to extract YouTube Id to translate to embedded video.
* Based on http://stackoverflow.com/questions/3452546/javascript-regex-how-to-get-youtube-video-id-from-url

View File

@ -1258,6 +1258,40 @@ export class CoreDomUtilsProvider {
}
/**
* Wait for images to load.
*
* @param {HTMLElement} element The element to search in.
* @return {Promise<boolean>} Promise resolved with a boolean: whether there was any image to load.
*/
waitForImages(element: HTMLElement): Promise<boolean> {
const imgs = Array.from(element.querySelectorAll('img')),
promises = [];
let hasImgToLoad = false;
imgs.forEach((img) => {
if (img && !img.complete) {
hasImgToLoad = true;
// Wait for image to load or fail.
promises.push(new Promise((resolve, reject): void => {
const imgLoaded = (): void => {
resolve();
img.removeEventListener('load', imgLoaded);
img.removeEventListener('error', imgLoaded);
};
img.addEventListener('load', imgLoaded);
img.addEventListener('error', imgLoaded);
}));
}
});
return Promise.all(promises).then(() => {
return hasImgToLoad;
});
}
/**
* Wrap an HTMLElement with another element.
*