Show more than one questions in the same page

From Catglobe Wiki
Jump to: navigation, search

Introduction

There is a common need of showing more than one questions in the same page, but unfortunately it is not supported by the current editor (we hope it will be possible with the new viewer in a near future)

In the mean time, we need to have some work-arround tips to make it work.

Solution 1 : (old setup and not in use any longer)

The main idea is that when we want to display a question not in its own page, there must be an input element added to that page's html with name following a specific format: non grid question should have name as QUESTION.question_label, grid questions use names as QUESTION.question_label.grid_index.
It is very important that questions to be displayed in other pages must be made as DUMMY.
Multi/Multi grid questions are special cases, it is not good enough if we only add checkboxes with the right name format, we also need to add one hidden input for each multi/multi grid's sub questions.

Example 1:

I want to show my single question labeled Q1 in another question with radio buttons, I need to make sure the HTML contains the following code for each answer option.

<tr>
   <td width="16px" valign="top" align="center">
      <input type="radio" value="1" name="QUESTION.Q1">
   </td>
   <td valign="top" align="left">
      Answer option text 1
   </td>
</tr>

Example 2:
I want to show a single grid question label Q1 in another question, I need to make sure the HTML contains the following code for each answer option and each sub question. Q1 has 2 answer options

<tr>
   <td width="16px" valign="top" align="center">
      <input type="radio" value="1" name="QUESTION.Q1.0">
   </td>
   <td width="16px" valign="top" align="center">
      <input type="radio" value="2" name="QUESTION.Q1.0">
   </td>

</tr>

Example 3:
I want to show a multi question label Q1 in another question, Q1 has 2 answer options, I need to make sure the HTML contains the following code for each answer option.

<tr>
   <td width="16px" valign="top" align="center">
      <input type="checkbox" value="1" name="QUESTION.Q1">
   </td>
   <td width="16px" valign="top" align="center">
      Answer option 1
   </td>
</tr>

Furthermore, we need to add an hidden input element like this.

<input type="hidden" name="QUESTION.Q1"></input>

There are basically 3 ways of changing a question's HTML.

Use HTML

This one is obvious, you can use HTML Editor to edit a question's text to include other questions.
Due to the fact that non-page questions have specific generated HTML code based on their answer options, it is usually hard to inject other questions and to make it look nice enough.
This method is usualy used when using a page question to display other questions, and it is convenient to use when the requirements are complex and the texts are not likely to change much. This one is the fastest solution among 3
The disadvantage of this method is that it is difficult to modify if the HTML is too complex.

quest.onInit

This function is called after the HTML of the question is generated, therefore it is the best to use JQuery to insert the elements added to the HTML.
It would be useful to use tools like Firebug to know the ids/names of the elements where we want to insert.
This solution should be used when there is not much to change the existing HTML of the question, for example: we want to show a text grid question in another text grid question which has the same set of sub question texts.

quest.getHTML

This method is to override the HTML generated by the system. This is similar to the first solution, but different in several things: this can be used with any question types; it might be more difficult to start with and not as obvious; it is effective when there are lots of elements that should be generated are similar to each others (use loops)

Code sample

Open the qnaire "Js demo - some js samples" (Resource Id: 159684). View the Question "Showhtmlcode"

Solution 2 :

The main idea is that when we want to display a question not in its own page, we will use 1 dummy question to store all question text, answer option text that we need to put and input them as JSON format. After that, we will load the value on this question via jquery to initialize the questions.
It is very important that questions to be displayed in other pages must be made as DUMMY.

For e.g, I will add the text grid question for validating the email into another text grid question :

Step 1 : Create a dummy text grid question for storing the email value

Text grid question.jpg

Step 2 : Create a dummy text question for storing the JSON

string language = Context_getSelectedLanguage();
string label;
number i;
number n;
string result = "var q = {";
string js;
array q;
//-------GENERATE JSON FOR Email QUESTION ----------
q = Question_getQuestion("D_Email", language);
js = "{"
 + "\\\"label\\\":\\\"D_Email.0\\\","
 + "\\\"text\\\":\\\"" + decodeHtmlEntities(stripHtml(q[QUESTION_SUB_QUESTIONS][0][SUBQUESTION_TEXT])) + "\\\""
 + "}";
result = result + "\\\"email0\\\":" + js + ",";

js = "{"
 + "\\\"label\\\":\\\"D_Email.1\\\","
 + "\\\"text\\\":\\\"" + decodeHtmlEntities(stripHtml(q[QUESTION_SUB_QUESTIONS][1][SUBQUESTION_TEXT])) + "\\\""
 + "}";
result = result + "\\\"email1\\\":" + js ;
result = result + "}";
D_InitializeJSON = result;

Step 3 : Load the JSON on the destination question

On the destination question , open its JS tab and input the code like below :

eval("{{D_InitializeJSON}}");

function TextQuestion(q, selectedValue)
{
   if (q.label.indexOf("QUESTION.") != -1)
      this.label = q.label;
   else
      this.label = "QUESTION." + q.label;
   
   this.selectedValue = selectedValue;	   
   this.text = q.text;
   $("input[name='" + this.label + "']").val(selectedValue);
}

TextQuestion.prototype.getHTML_Row = function(even)
{
   var row = $("<tr>");
   var s;
   
   if (even)
      s = "even";
   else
      s = "odd";
   row.append($("<td>").text(this.text).addClass("grid_subquestion_text grid_subquestion_"+s));
   var input = $("<input type = 'text'>");
   input.attr("name", this.label).val(this.selectedValue);
   row.append(
      $("<td>").addClass("grid_subquestion_"+ s)
      .append(input)
   );

   return row;
}

quest.onInit = function()
{
    $(".grid_inner").append(new TextQuestion(q.email0, "{{D_Email[0].Value}}").getHTML_Row(true));
    $(".grid_inner").append(new TextQuestion(q.email1, "{{D_Email[1].Value}}").getHTML_Row(false));      

    $("input:text").width("400px");
    $(".grid_inner").find("tr").each(
      function(i)
      {
         if(i>3) i+=1;
         if(i%2==0)
            $(this).children().removeClass("grid_subquestion_odd").addClass("grid_subquestion_even");
         else
            $(this).children().removeClass("grid_subquestion_even").addClass("grid_subquestion_odd");
      }
   ); 
}

var defaultQuestionCheck = questioncheck;
var questioncheck = function()
{
   ErrorMessages.getInstance().clearErrorMessages();
   var b =true;
   if (!defaultQuestionCheck())
      return false;
     var email = $("input[name$='QUESTION.D_Email.0']").val();   
     if (!validateEmail(email))
     {
        ErrorMessages.getInstance().showErrorMessage(ErrorMessage_InvalidEmailFormat);
        b= false;
     }

     if ($("input[name$='QUESTION.D_Email.1']").val() != email)
     {
        ErrorMessages.getInstance().showErrorMessage(ErrorMessage_EmailNotMatch);
        b= false;
     }
   return b;
}

Code sample

Open the qnaire "Js demo - some js samples" (Resource Id: 159684). View the Question "Q1"

Testing

Remember to always test your work on these following cases:

  • Display correctly as expected
  • Save the right value in the right place
  • Display previously saved values
  • Validate inputs
  • Update previously saved values