MDL-73597 Questions: Form checker integration for D&D question types

This commit is contained in:
Huong Nguyen
2022-03-16 16:29:19 +07:00
parent 6d7dbaa685
commit 7d764cde8b
9 changed files with 180 additions and 6 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -34,6 +34,7 @@ define(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys
*/
function DragDropOntoImageQuestion(containerId, readOnly, places) {
this.containerId = containerId;
this.questionAnswer = {};
M.util.js_pending('qtype_ddimageortext-init-' + this.containerId);
this.places = places;
this.allImagesLoaded = false;
@@ -301,6 +302,48 @@ define(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys
var drop = root.find('.dropzone.place' + place);
thisQ.sendDragToDrop(unplacedDrag, drop);
});
// Save the question answer.
thisQ.questionAnswer = thisQ.getQuestionAnsweredValues();
};
/**
* Get the question answered values.
*
* @return {Object} Contain key-value with key is the input id and value is the input value.
*/
DragDropOntoImageQuestion.prototype.getQuestionAnsweredValues = function() {
let result = {};
this.getRoot().find('input.placeinput').each((i, inputNode) => {
result[inputNode.id] = inputNode.value;
});
return result;
};
/**
* Check if the question is being interacted or not.
*
* @return {boolean} Return true if the user has changed the question-answer.
*/
DragDropOntoImageQuestion.prototype.isQuestionInteracted = function() {
const oldAnswer = this.questionAnswer;
const newAnswer = this.getQuestionAnsweredValues();
let isInteracted = false;
// First, check both answers have the same structure or not.
if (JSON.stringify(newAnswer) !== JSON.stringify(oldAnswer)) {
isInteracted = true;
return isInteracted;
}
// Check the values.
Object.keys(newAnswer).forEach(key => {
if (newAnswer[key] !== oldAnswer[key]) {
isInteracted = true;
}
});
return isInteracted;
};
/**
@@ -1131,6 +1174,12 @@ define(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys
if (questionManager.isKeyboardNavigation) {
questionManager.isKeyboardNavigation = false;
}
if (thisQ.isQuestionInteracted()) {
// The user has interacted with the draggable items. We need to mark the form as dirty.
questionManager.handleFormDirty();
// Save the new answered value.
thisQ.questionAnswer = thisQ.getQuestionAnsweredValues();
}
},
/**
@@ -1141,6 +1190,15 @@ define(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys
getQuestionForEvent: function(e) {
var containerId = $(e.currentTarget).closest('.que.ddimageortext').attr('id');
return questionManager.questions[containerId];
},
/**
* Handle when the form is dirty.
*/
handleFormDirty: function() {
if (typeof M.core_formchangechecker !== 'undefined') {
M.core_formchangechecker.set_form_changed();
}
}
};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -41,6 +41,7 @@ define(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes', 'core/key_codes'], f
this.shapes = [];
this.shapeSVGs = [];
this.isPrinting = false;
this.questionAnswer = {};
if (readOnly) {
this.getRoot().addClass('qtype_ddmarker-readonly');
}
@@ -150,6 +151,48 @@ define(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes', 'core/key_codes'], f
thisQ.cloneDragIfNeeded(drag);
}
});
// Save the question answer.
thisQ.questionAnswer = thisQ.getQuestionAnsweredValues();
};
/**
* Get the question answered values.
*
* @return {Object} Contain key-value with key is the input id and value is the input value.
*/
DragDropMarkersQuestion.prototype.getQuestionAnsweredValues = function() {
let result = {};
this.getRoot().find('input.choices').each((i, inputNode) => {
result[inputNode.id] = inputNode.value;
});
return result;
};
/**
* Check if the question is being interacted or not.
*
* @return {boolean} Return true if the user has changed the question-answer.
*/
DragDropMarkersQuestion.prototype.isQuestionInteracted = function() {
const oldAnswer = this.questionAnswer;
const newAnswer = this.getQuestionAnsweredValues();
let isInteracted = false;
// First, check both answers have the same structure or not.
if (JSON.stringify(newAnswer) !== JSON.stringify(oldAnswer)) {
isInteracted = true;
return isInteracted;
}
// Check the values.
Object.keys(newAnswer).forEach(key => {
if (newAnswer[key] !== oldAnswer[key]) {
isInteracted = true;
}
});
return isInteracted;
};
/**
@@ -320,6 +363,12 @@ define(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes', 'core/key_codes'], f
}
this.getRoot().find('input.choice' + choiceNo).val(coords.join(';'));
if (this.isQuestionInteracted()) {
// The user has interacted with the draggable items. We need to mark the form as dirty.
questionManager.handleFormDirty();
// Save the new answered value.
this.questionAnswer = this.getQuestionAnsweredValues();
}
};
/**
@@ -844,6 +893,15 @@ define(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes', 'core/key_codes'], f
getQuestionForEvent: function(e) {
var containerId = $(e.currentTarget).closest('.que.ddmarker').attr('id');
return questionManager.questions[containerId];
},
/**
* Handle when the form is dirty.
*/
handleFormDirty: function() {
if (typeof M.core_formchangechecker !== 'undefined') {
M.core_formchangechecker.set_form_changed();
}
}
};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+58
View File
@@ -50,6 +50,7 @@ define(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys
*/
function DragDropToTextQuestion(containerId, readOnly) {
this.containerId = containerId;
this.questionAnswer = {};
if (readOnly) {
this.getRoot().addClass('qtype_ddwtos-readonly');
}
@@ -187,6 +188,48 @@ define(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys
// Send the drag to drop.
thisQ.sendDragToDrop(thisQ.getUnplacedChoice(thisQ.getGroup(input), choice), drop);
});
// Save the question answer.
thisQ.questionAnswer = thisQ.getQuestionAnsweredValues();
};
/**
* Get the question answered values.
*
* @return {Object} Contain key-value with key is the input id and value is the input value.
*/
DragDropToTextQuestion.prototype.getQuestionAnsweredValues = function() {
let result = {};
this.getRoot().find('input.placeinput').each((i, inputNode) => {
result[inputNode.id] = inputNode.value;
});
return result;
};
/**
* Check if the question is being interacted or not.
*
* @return {boolean} Return true if the user has changed the question-answer.
*/
DragDropToTextQuestion.prototype.isQuestionInteracted = function() {
const oldAnswer = this.questionAnswer;
const newAnswer = this.getQuestionAnsweredValues();
let isInteracted = false;
// First, check both answers have the same structure or not.
if (JSON.stringify(newAnswer) !== JSON.stringify(oldAnswer)) {
isInteracted = true;
return isInteracted;
}
// Check the values.
Object.keys(newAnswer).forEach(key => {
if (newAnswer[key] !== oldAnswer[key]) {
isInteracted = true;
}
});
return isInteracted;
};
/**
@@ -868,6 +911,21 @@ define(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys
if (questionManager.isKeyboardNavigation) {
questionManager.isKeyboardNavigation = false;
}
if (thisQ.isQuestionInteracted()) {
// The user has interacted with the draggable items. We need to mark the form as dirty.
questionManager.handleFormDirty();
// Save the new answered value.
thisQ.questionAnswer = thisQ.getQuestionAnsweredValues();
}
},
/**
* Handle when the form is dirty.
*/
handleFormDirty: function() {
if (typeof M.core_formchangechecker !== 'undefined') {
M.core_formchangechecker.set_form_changed();
}
}
};