MOBILE-2389 qtype: Implement calculated and calculatedsimple types
parent
a89737a5d3
commit
38184308a3
|
@ -0,0 +1,46 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { IonicModule } from 'ionic-angular';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { AddonQtypeCalculatedHandler } from './providers/handler';
|
||||||
|
import { AddonQtypeCalculatedComponent } from './component/calculated';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonQtypeCalculatedComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
IonicModule,
|
||||||
|
TranslateModule.forChild(),
|
||||||
|
CoreDirectivesModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
AddonQtypeCalculatedHandler
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AddonQtypeCalculatedComponent
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
AddonQtypeCalculatedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AddonQtypeCalculatedModule {
|
||||||
|
constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeCalculatedHandler) {
|
||||||
|
questionDelegate.registerHandler(handler);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<section ion-list class="addon-qtype-calculated-container" *ngIf="question.text || question.text === ''">
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.text"></core-format-text></p>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<!-- Display unit options before the answer input. -->
|
||||||
|
<ng-container *ngIf="question.options && question.options.length && question.optionsFirst">
|
||||||
|
<ng-container *ngTemplateOutlet="radioUnits"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ion-item text-wrap>
|
||||||
|
<ion-row>
|
||||||
|
<!-- Display unit select before the answer input. -->
|
||||||
|
<ng-container *ngIf="question.select && question.selectFirst">
|
||||||
|
<ng-container *ngTemplateOutlet="selectUnits"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Input to enter the answer. -->
|
||||||
|
<ion-col>
|
||||||
|
<ion-input type="text" placeholder="{{ 'core.question.answer' | translate }}" [attr.name]="question.input.name" [value]="question.input.value" [disabled]="question.input.readOnly" [ngClass]='{"core-question-answer-correct": question.input.isCorrect === 1, "core-question-answer-incorrect": question.input.isCorrect === 0}' autocorrect="off">
|
||||||
|
</ion-input>
|
||||||
|
</ion-col>
|
||||||
|
|
||||||
|
<!-- Display unit select after the answer input. -->
|
||||||
|
<ng-container *ngIf="question.select && !question.selectFirst">
|
||||||
|
<ng-container *ngTemplateOutlet="selectUnits"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</ion-row>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<!-- Display unit options after the answer input. -->
|
||||||
|
<ng-container *ngIf="question.options && question.options.length && !question.optionsFirst">
|
||||||
|
<ng-container *ngTemplateOutlet="radioUnits"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Template for units entered using a select. -->
|
||||||
|
<ng-template #selectUnits>
|
||||||
|
<ion-col>
|
||||||
|
<label *ngIf="question.select.accessibilityLabel" class="accesshide" for="{{question.select.id}}">{{ question.select.accessibilityLabel }}</label>
|
||||||
|
<ion-select id="{{question.select.id}}" [name]="question.select.name" [ngModel]="question.select.selected">
|
||||||
|
<ion-option *ngFor="let option of question.select.options" [value]="option.value">{{option.label}}</ion-option>
|
||||||
|
</ion-select>
|
||||||
|
<!-- @todo: select fix? -->
|
||||||
|
</ion-col>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<!-- Template for units entered using radio buttons. -->
|
||||||
|
<ng-template #radioUnits>
|
||||||
|
<div radio-group [ngModel]="question.unit" [name]="question.optionsName">
|
||||||
|
<ion-radio *ngFor="let option of question.options" [value]="option.value" [disabled]="option.disabled">
|
||||||
|
<p>{{option.text}}</p>
|
||||||
|
</ion-radio>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
|
@ -0,0 +1,40 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||||
|
import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to render a calculated question.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'addon-qtype-calculated',
|
||||||
|
templateUrl: 'calculated.html'
|
||||||
|
})
|
||||||
|
export class AddonQtypeCalculatedComponent extends CoreQuestionBaseComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor(logger: CoreLoggerProvider, questionHelper: CoreQuestionHelperProvider, domUtils: CoreDomUtilsProvider) {
|
||||||
|
super(logger, 'AddonQtypeCalculatedComponent', questionHelper, domUtils);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.initCalculatedComponent();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable, Injector } from '@angular/core';
|
||||||
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
|
import { CoreQuestionHandler } from '@core/question/providers/delegate';
|
||||||
|
import { AddonQtypeNumericalHandler } from '@addon/qtype/numerical/providers/handler';
|
||||||
|
import { AddonQtypeCalculatedComponent } from '../component/calculated';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support calculated question type.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonQtypeCalculatedHandler implements CoreQuestionHandler {
|
||||||
|
name = 'AddonQtypeCalculated';
|
||||||
|
type = 'qtype_calculated';
|
||||||
|
|
||||||
|
constructor(private utils: CoreUtilsProvider, private numericalHandler: AddonQtypeNumericalHandler) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Component to use to display the question.
|
||||||
|
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @param {any} question The question to render.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector, question: any): any | Promise<any> {
|
||||||
|
return AddonQtypeCalculatedComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a response is complete.
|
||||||
|
*
|
||||||
|
* @param {any} question The question.
|
||||||
|
* @param {any} answers Object with the question answers (without prefix).
|
||||||
|
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
|
||||||
|
*/
|
||||||
|
isCompleteResponse(question: any, answers: any): number {
|
||||||
|
// This question type depends on numerical.
|
||||||
|
if (this.isGradableResponse(question, answers) === 0 || !this.numericalHandler.validateUnits(answers['answer'])) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.requiresUnits(question)) {
|
||||||
|
return this.isValidValue(answers['unit']) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a student has provided enough of an answer for the question to be graded automatically,
|
||||||
|
* or whether it must be considered aborted.
|
||||||
|
*
|
||||||
|
* @param {any} question The question.
|
||||||
|
* @param {any} answers Object with the question answers (without prefix).
|
||||||
|
* @return {number} 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||||
|
*/
|
||||||
|
isGradableResponse(question: any, answers: any): number {
|
||||||
|
// This question type depends on numerical.
|
||||||
|
let isGradable = this.isValidValue(answers['answer']);
|
||||||
|
if (isGradable && this.requiresUnits(question)) {
|
||||||
|
// The question requires a unit.
|
||||||
|
isGradable = this.isValidValue(answers['unit']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isGradable ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if two responses are the same.
|
||||||
|
*
|
||||||
|
* @param {any} question Question.
|
||||||
|
* @param {any} prevAnswers Object with the previous question answers.
|
||||||
|
* @param {any} newAnswers Object with the new question answers.
|
||||||
|
* @return {boolean} Whether they're the same.
|
||||||
|
*/
|
||||||
|
isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean {
|
||||||
|
// This question type depends on numerical.
|
||||||
|
return this.utils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'answer') &&
|
||||||
|
this.utils.sameAtKeyMissingIsBlank(prevAnswers, newAnswers, 'unit');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a value is valid (not empty).
|
||||||
|
*
|
||||||
|
* @param {string|number} value Value to check.
|
||||||
|
* @return {boolean} Whether the value is valid.
|
||||||
|
*/
|
||||||
|
isValidValue(value: string | number): boolean {
|
||||||
|
return !!value || value === '0' || value === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a question requires units in a separate input.
|
||||||
|
*
|
||||||
|
* @param {any} question The question.
|
||||||
|
* @return {boolean} Whether the question requires units.
|
||||||
|
*/
|
||||||
|
requiresUnits(question: any): boolean {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = question.html;
|
||||||
|
|
||||||
|
return !!(div.querySelector('select[name*=unit]') || div.querySelector('input[type="radio"]'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
|
||||||
|
import { AddonQtypeCalculatedSimpleHandler } from './providers/handler';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
AddonQtypeCalculatedSimpleHandler
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonQtypeCalculatedSimpleModule {
|
||||||
|
constructor(questionDelegate: CoreQuestionDelegate, handler: AddonQtypeCalculatedSimpleHandler) {
|
||||||
|
questionDelegate.registerHandler(handler);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable, Injector } from '@angular/core';
|
||||||
|
import { CoreQuestionHandler } from '@core/question/providers/delegate';
|
||||||
|
import { AddonQtypeCalculatedHandler } from '@addon/qtype/calculated/providers/handler';
|
||||||
|
import { AddonQtypeCalculatedComponent } from '@addon/qtype/calculated/component/calculated';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support calculated simple question type.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonQtypeCalculatedSimpleHandler implements CoreQuestionHandler {
|
||||||
|
name = 'AddonQtypeCalculatedSimple';
|
||||||
|
type = 'qtype_calculatedsimple';
|
||||||
|
|
||||||
|
constructor(private calculatedHandler: AddonQtypeCalculatedHandler) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Component to use to display the question.
|
||||||
|
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @param {any} question The question to render.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector, question: any): any | Promise<any> {
|
||||||
|
// Calculated simple behaves like a calculated, use the same component.
|
||||||
|
return AddonQtypeCalculatedComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a response is complete.
|
||||||
|
*
|
||||||
|
* @param {any} question The question.
|
||||||
|
* @param {any} answers Object with the question answers (without prefix).
|
||||||
|
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
|
||||||
|
*/
|
||||||
|
isCompleteResponse(question: any, answers: any): number {
|
||||||
|
// This question type depends on calculated.
|
||||||
|
return this.calculatedHandler.isCompleteResponse(question, answers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a student has provided enough of an answer for the question to be graded automatically,
|
||||||
|
* or whether it must be considered aborted.
|
||||||
|
*
|
||||||
|
* @param {any} question The question.
|
||||||
|
* @param {any} answers Object with the question answers (without prefix).
|
||||||
|
* @return {number} 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||||
|
*/
|
||||||
|
isGradableResponse(question: any, answers: any): number {
|
||||||
|
// This question type depends on calculated.
|
||||||
|
return this.calculatedHandler.isGradableResponse(question, answers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if two responses are the same.
|
||||||
|
*
|
||||||
|
* @param {any} question Question.
|
||||||
|
* @param {any} prevAnswers Object with the previous question answers.
|
||||||
|
* @param {any} newAnswers Object with the new question answers.
|
||||||
|
* @return {boolean} Whether they're the same.
|
||||||
|
*/
|
||||||
|
isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean {
|
||||||
|
// This question type depends on calculated.
|
||||||
|
return this.calculatedHandler.isSameResponse(question, prevAnswers, newAnswers);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,9 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { AddonQtypeCalculatedModule } from './calculated/calculated.module';
|
||||||
import { AddonQtypeCalculatedMultiModule } from './calculatedmulti/calculatedmulti.module';
|
import { AddonQtypeCalculatedMultiModule } from './calculatedmulti/calculatedmulti.module';
|
||||||
|
import { AddonQtypeCalculatedSimpleModule } from './calculatedsimple/calculatedsimple.module';
|
||||||
import { AddonQtypeDescriptionModule } from './description/description.module';
|
import { AddonQtypeDescriptionModule } from './description/description.module';
|
||||||
import { AddonQtypeMatchModule } from './match/match.module';
|
import { AddonQtypeMatchModule } from './match/match.module';
|
||||||
import { AddonQtypeMultichoiceModule } from './multichoice/multichoice.module';
|
import { AddonQtypeMultichoiceModule } from './multichoice/multichoice.module';
|
||||||
|
@ -25,7 +27,9 @@ import { AddonQtypeTrueFalseModule } from './truefalse/truefalse.module';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
imports: [
|
imports: [
|
||||||
|
AddonQtypeCalculatedModule,
|
||||||
AddonQtypeCalculatedMultiModule,
|
AddonQtypeCalculatedMultiModule,
|
||||||
|
AddonQtypeCalculatedSimpleModule,
|
||||||
AddonQtypeDescriptionModule,
|
AddonQtypeDescriptionModule,
|
||||||
AddonQtypeMatchModule,
|
AddonQtypeMatchModule,
|
||||||
AddonQtypeMultichoiceModule,
|
AddonQtypeMultichoiceModule,
|
||||||
|
|
|
@ -36,6 +36,124 @@ export class CoreQuestionBaseComponent {
|
||||||
this.logger = logger.getInstance(logName);
|
this.logger = logger.getInstance(logName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a question component of type calculated or calculated simple.
|
||||||
|
*
|
||||||
|
* @return {void|HTMLElement} Element containing the question HTML, void if the data is not valid.
|
||||||
|
*/
|
||||||
|
initCalculatedComponent(): void | HTMLElement {
|
||||||
|
// Treat the input text first.
|
||||||
|
const questionDiv = this.initInputTextComponent();
|
||||||
|
if (questionDiv) {
|
||||||
|
|
||||||
|
// Check if the question has a select for units.
|
||||||
|
const selectModel: any = {},
|
||||||
|
select = <HTMLSelectElement> questionDiv.querySelector('select[name*=unit]'),
|
||||||
|
options = select && Array.from(select.querySelectorAll('option'));
|
||||||
|
|
||||||
|
if (select && options && options.length) {
|
||||||
|
|
||||||
|
selectModel.id = select.id;
|
||||||
|
selectModel.name = select.name;
|
||||||
|
selectModel.disabled = select.disabled;
|
||||||
|
selectModel.options = [];
|
||||||
|
|
||||||
|
// Treat each option.
|
||||||
|
for (const i in options) {
|
||||||
|
const optionEl = options[i];
|
||||||
|
|
||||||
|
if (typeof optionEl.value == 'undefined') {
|
||||||
|
this.logger.warn('Aborting because couldn\'t find input.', this.question.name);
|
||||||
|
|
||||||
|
return this.questionHelper.showComponentError(this.onAbort);
|
||||||
|
}
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
value: optionEl.value,
|
||||||
|
label: optionEl.innerHTML
|
||||||
|
};
|
||||||
|
|
||||||
|
if (optionEl.selected) {
|
||||||
|
selectModel.selected = option.value;
|
||||||
|
selectModel.selectedLabel = option.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectModel.options.push(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectModel.selected) {
|
||||||
|
// No selected option, select the first one.
|
||||||
|
selectModel.selected = selectModel.options[0].value;
|
||||||
|
selectModel.selectedLabel = selectModel.options[0].label;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the accessibility label.
|
||||||
|
const accessibilityLabel = questionDiv.querySelector('label[for="' + select.id + '"]');
|
||||||
|
selectModel.accessibilityLabel = accessibilityLabel && accessibilityLabel.innerHTML;
|
||||||
|
|
||||||
|
this.question.select = selectModel;
|
||||||
|
|
||||||
|
// Check which one should be displayed first: the select or the input.
|
||||||
|
const input = questionDiv.querySelector('input[type="text"][name*=answer]');
|
||||||
|
this.question.selectFirst =
|
||||||
|
questionDiv.innerHTML.indexOf(input.outerHTML) > questionDiv.innerHTML.indexOf(select.outerHTML);
|
||||||
|
|
||||||
|
return questionDiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the question has radio buttons for units.
|
||||||
|
const radios = <HTMLInputElement[]> Array.from(questionDiv.querySelectorAll('input[type="radio"]'));
|
||||||
|
if (!radios.length) {
|
||||||
|
// No select and no radio buttons. The units need to be entered in the input text.
|
||||||
|
return questionDiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.question.options = [];
|
||||||
|
|
||||||
|
for (const i in radios) {
|
||||||
|
const radioEl = radios[i],
|
||||||
|
option: any = {
|
||||||
|
id: radioEl.id,
|
||||||
|
name: radioEl.name,
|
||||||
|
value: radioEl.value,
|
||||||
|
checked: radioEl.checked,
|
||||||
|
disabled: radioEl.disabled
|
||||||
|
},
|
||||||
|
// Get the label with the question text.
|
||||||
|
label = <HTMLElement> questionDiv.querySelector('label[for="' + option.id + '"]');
|
||||||
|
|
||||||
|
this.question.optionsName = option.name;
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
option.text = label.innerText;
|
||||||
|
|
||||||
|
// Check that we were able to successfully extract options required data.
|
||||||
|
if (typeof option.name != 'undefined' && typeof option.value != 'undefined' &&
|
||||||
|
typeof option.text != 'undefined') {
|
||||||
|
|
||||||
|
if (radioEl.checked) {
|
||||||
|
// If the option is checked we use the model to select the one.
|
||||||
|
this.question.unit = option.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.question.options.push(option);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Something went wrong when extracting the questions data. Abort.
|
||||||
|
this.logger.warn('Aborting because of an error parsing options.', this.question.name, option.name);
|
||||||
|
|
||||||
|
return this.questionHelper.showComponentError(this.onAbort);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check which one should be displayed first: the options or the input.
|
||||||
|
const input = questionDiv.querySelector('input[type="text"][name*=answer]');
|
||||||
|
this.question.optionsFirst =
|
||||||
|
questionDiv.innerHTML.indexOf(input.outerHTML) > questionDiv.innerHTML.indexOf(options[0].outerHTML);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the component and the question text.
|
* Initialize the component and the question text.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue