How to create a custom question-type to the survey: Optional Text question

From Sense/Net Wiki
Jump to: navigation, search
  • 100%
  • 6.5.5.
  • Enterprise
  • Community
  • Planned


Optional Question in the Survey
If a new question type needs to be implemented to the survey, as a developer it can be carried out easily. Since the javascript engine of the survey uses templated field-definitions in separated files, so the solution can be expanded by adding a new type.


Create the new field definition

When creating a new question-type, the unique JS field-template must be connected to sn.fields.js, by importing it at the top of your file:

// using $skin/scripts/sn/sn.fields.js

This template will describe the question’s appearance and functionality on the frontend, and it must be designed to be able to serve the field operation mechanisms on the backend. On the first level, the question-related parameters must to be described:

  • the name of question type
  • The title of question (possibly as a localized string resource)
  • The icon name of the question (will define the class applied on the DOM element for displaying the icon)
  • editor template of question
  • Fill template of question
SN.Fields.OptionalText = { 
    name: 'optionaltext', 
    title: SN.Resources.SurveyList["OptionalTextQuestion-DisplayName"], 
    icon: 'optionaltext', 
    editor: {}, 
    fill: {}, 

Editor section, preset Fields of question

The possible fields of the question are defined in the editor.schema.fields section:

Type (with predefined type and default value):

Type: { type: "string", defaultValue: "OptionalText" },

Control (with predefined type and default value):

Control: { type: "string", defaultValue: "OptionalText" },

Id (with predefined type):

Id: { type: 'string' },

Title (with predefined type and default value, possibly as a resource):

Title: { type: "string", defaultValue: SN.Resources.SurveyList["UntitledQuestion"] },

Hint (with predefined type):

Hint: { type: "string" },

PlaceHolder (with predefined type):

PlaceHolder: { type: "string" },

Required (with predefined type and default value):

Required: { type: "boolean", defaultValue: false },

CustomRequired: calls the string value of the applied custom-validation rule, that is defined in the editor.schema.validation section:

CustomRequired: { defaultValue: "optional-text-required" },

Options (storing all option parameters in defaultValue section) by those the question will be displayed, when created newly. Optional text question has two default values, that are created originally by

  • title : that is a localized string resource, defines the default value of option title,
  • default value: which is true or false defines the default value of option,
  • value: that is another localized string resource, defines the default value of option label,
  • text: which is an empty string (here will be added the optional text-answer, as an empty value by default, when the question is being answered in the survey)
Options: { 
                    defaultValue: [ 
                        title: SN.Resources.SurveyList["YesValue"], 
                        defaultValue: false, 
                        value: SN.Resources.SurveyList["YesValue"], 
                        text: '' 
                        title: SN.Resources.SurveyList["NoValue"], 
                        defaultValue: false, 
                        value: SN.Resources.SurveyList["NoValue"] 

SNFields section stores the array of SN Fields, that’s value will be set in the backend:

SNFields: ['DisplayName', 'Description', 'Required', 'Options']


Settings contains the settings parameters of question-type, and the save function to store settings applied for the specific question. Basically all of them are defined in the editor.schema.fields.Settings section. The render function, that is defined here, will use the the template of the unordered-list, where question-options will be displayed by the option values when question is created and edited.

template: '<ul class="sn-survey-choicequestion-options"></ul>',

This is the engine of all specific parameters and optional behavior. This control will display all choice-options, also the optional textbox, how it is defined previously in the “Options” part. The Settings function contains the render, that will describe the edit/create functionality of the question, displays all options as were described in the option-list, and holds the save function, which pushes the question-data to the survey-related JSON.

render: function ($question) { 
    var survey = $('#surveyContainer').data('Survey'); 
    var template = SN.Fields.OptionalText.fill.template; 
    var renderingFunction = SN.Fields.OptionalText.fill.render; 
    var id = $question.closest('.sn-survey-section').attr('id'); 
    var section = survey.getSectionById(id); 
    var questionId = $question.attr('id'); 
    var question = survey.getQuestionById(section, questionId); 
    for (var i = 0; i < SN.Fields.OptionalText.editor.schema.fields.Options.defaultValue.length; i++) { 
        var option = SN.Fields.OptionalText.editor.schema.fields.Options.defaultValue[i]; 
        var row = $('<li class="option-row"><input type="radio" disabled name="optionaltext"  id="' + option.value + '"  /><input type="text" class="option-title option-title-' + option.value + '" value="' + option.value + '"  id="' + option.value + '-title"/></li>').appendTo($question.find('.sn-survey-choicequestion-options')); 
        if (typeof option.text !== 'undefined') 
            row.append('<p class="optional-text-info">' + SN.Resources.SurveyList["OptionalTextInfo"] + '</p>'); 
    $question.find('input, select').on('input', save); 
    setTimeout(function () { 
        survey.refreshPreview(questionId, template, question, renderingFunction); 
    }, 500) 
    function save() { 
        var options = []; 
        for (var x = 0; x < $question.find('.option-row').length; x++) { 
            var $option = $question.find('.sn-survey-choicequestion-options .option-row').eq(x); 
            var $textInput = $option.find('p'); 
            var option = {}; 
            option.title = $option.find('.option-title').val(); 
            if (typeof option.title === 'undefined' || option.title.length === 0) 
                option.title = $option.find('.option-title').text(); 
            if ($textInput.length > 0) 
                option.text = ''; 
        question = survey.getQuestionById(section, questionId); 
        question.Options = options; 
        var question = survey.getQuestionById(section, questionId); 
        survey.refreshPreview(questionId, template, question, renderingFunction); 

Question validation

The Rule and Value of general validation is described here by naming its rule with a string resource, and setting the default parameter of validation as false.

Validation: { 
    Rule: { type: "string", value: "optional-text-required" }, 
    Value: { type: "boolean", defaultValue: false } 

Further validation mechanism is ruled in the editor-schema-validation section. It finds the validation rule by name, and defines the validation conditions as its method function:

validation: { 
    fields: { 
        Type: [ 
            name: 'validation', 
            rules: [ 

In the editor schema will be defined the custom validation rule, we want to apply, when validation is switched on while creating the question. By this question-type only one validation-rule is defined, so let’s go into the details of this one! In the validation-type’s „rules” section we name the validation rule by that will be invited in the editor-schema.fields.CustomRequired part.

name: 'optional-text-required',

The Type needs to be set to „Boolean”:

type: 'boolean',

The compliant SNField name is set to „Required”, so validation will run, if question is set to be required. The validation method is built up by the business logic of the question-type. If the question is set to required, will not be allowed to be saved without an answer. The JS function will select the specific question, and run the validation function on each submit or next-section click. The validation rule will examine, if there is a radio-button selected, and if so, it examines, if there is a textbox after the radio-button, that needs to be filled. If all parameters meet the requirements, the question is let to be saved.

snField: 'Required',

The text of error message is given as a localized string resource:

errorMessage: SN.Resources.SurveyList["OptionalTextRequired"],

Method contains the functionality of validation process. This JavaScript function will run on each Optional text type of survey question, where the required-switch as validation is switched on.

method: function (e) { 
    var $question = $(e).closest('.sn-question'); 
    var validate = $question.attr('data-optional-text-required'); 
    if (typeof validate !== 'undefined' && validate !== false) { 
        var value = $question.find('input[type="radio"]:checked').length; 
        if (value === 0) 
            return false; 
        else { 
            for (var i = 0; i < value; i++) { 
                var $option = $question.find('input[type="radio"]:checked').eq(i); 
                var $textbox = $option.siblings('.option-text'); 
                if ($textbox.length !== 0) { 
                    if ($textbox.val().length > 0) 
                        return true; 
                        return false; 
                    return true; 
    return true; 

When more types of validation is applied on a question, those will be listed in the type-array by different names. In each validation-type separate validation rules are listed in the rules-array. See: /Root/Global/scripts/sn/SN.Fields.ShortAnswer.js

Further parameters can be defined here, like Type, ErrorMessage, etc that are necessary to build up the validation construction of the unique question-type.


Editor section: question-template and menu

In this part of the editor section the HTML template of the question editor must be defined, by the use of templated title (#=Title#), id (#=Id#), and type (#=Type#) that is given by default in the editor.schema.fields section, or customized on the UI when a unique „Optional text” question is created and edited. String resources can be applied. Like the Placeholder value in the question-hint input.

Edit Optional Question in the Survey

By other question-types there will be found validation item in this menu, when the question can be validated. In this case, there must be defined a validation-section in the schema-section.

template: '<div class="sn-survey-question sn-survey-question-#=Type#" id="#=Id #">\ 
                   <div class="sn-survey-question-edit"> \ 
                       <input class="title" type="text" value="#=Title #" />\ 
                       <input class="hidden hint" type="text" placeHolder="' + SN.Resources.SurveyList["Hint-PlaceHolder"] + '" />\ 

In the question menu a hint field is added, so this will be the only menu item will be shown in an Optional text question’s menu when editing.

menu: [ 
        { field: 'Hint', text: SN.Resources.SurveyList["Hint-Menu"] } 

In the fields-section all necessary question-field-types must be listed, by granting it’s type and default value. Depending on question-type these can be Type, Controll, Id, Title, Hint, Placeholder, Required, Validation, Multiple, Options, Other, List, Settings, SNFields, This list can be expanded and customized with any other options as needed.

Fill template, browse view and operation of question type

The "Fill" section of the question template contains the HTML template that describes the layout of the question, rendered for completion. Contains templated values for title (#=Title#), id (#=Id#), and hint (#=Hint#) values, that get their values from the previously edited question.

Browse Optional Question in the Survey

When creating or editing a survey, this is the same template by the question preview is also rendered.

template: '<div class="sn-optional-answer sn-survey-question-elements" name="#=Id #" id="#=Id #"><label class="sn-title">#=Title #</label>\ 
                <ul name="#= Id#" class="sn-answer sn-optional-answer sn-survey-choicequestion-optioncontainer"></ul>\ 
                <span class="sn-hint">#= Hint #</span>\ 

Render holds the JavaScript function, which operates the question while it is answered, and these are the same rules, by the preview of question is generated in edit- or add new mode:

  • in editor mode it collects all question options to be able to render the question preview, and render them into the question’s option-container, by the listed options in editor.schema.fields.Options object
  • contains a function defined for click-event, that specifies the operation of options: check radio-buttons when clicked on each of the option-elements
  • run custom validation on changing radio-buttons’ status
  • disable textbox, in case the related option is not selected
render: function (id, mode, question) { 
    var survey = $('#surveyContainer').data('Survey'); 
    if (typeof question !== 'undefined') { 
        var options = question.Options; 
        if (mode === 'editor') { 
            var section = survey.getSectionById($('#' + id).closest('.sn-survey-section').attr('id')); 
            question = survey.getQuestionById(section, id); 
            options = question.Options; 
        var $container = $('.sn-question[data-qid="' + id + '"]').find('.sn-survey-choicequestion-optioncontainer'); 
        if ($container.length === 0) 
            $container = $('.sn-survey-question#' + id).find('.sn-survey-choicequestion-optioncontainer'); 
        for (var i = 0; i < options.length; i++) { 
            var value = survey.encodeString(options[i].title); 
            var $option = $('<li><input type="radio" value="' + value + '"><label>' + options[i].title + '</label></li>').appendTo($container); 
            if (typeof options[i].text !== 'undefined') 
                $option.append('<input class="option-text" type="text" />'); 
    $container.find('li').on('click', function () { 
        var $this = $(this); 
        var $radio = $this.find('input[type="radio"]'); 
        $radio.prop('checked', true); 
        if ($':checked')) { 
            //TODO: if any radio is checked, remove global invalid msg 
        $this.siblings('li').find('input[type = "radio"]').prop('checked', false); 
        var $textInput = $this.find('input[type="text"]'); 
        if ($textInput.length > 0) { 
            $textInput.attr('disabled', false); 
        else { 
            $container.find('input[type="text"]').attr('disabled', ''); 

Inside value section another JavaScript function can be found, which translates answers to savable data for the JSON, to store all answers by fillings in the content repository. By the very question, identified by its id, creates a data string, which will be added to the stored savable data variable that holds the whole survey’s data and stored in JSON format in the hidden textbox.

value: value: function ($question, questionId) { 
    var survey = $('#surveyContainer').data('Survey'); 
    var id = $question.closest('.sn-survey-section').attr('id'); 
    var section = survey.getSectionById(id); 
    var question = survey.getQuestionById(section, questionId); 
    var saveableData = ''; 
    var $checkedRadio = $question.find('input[type="radio"]:checked'); 
    var $textBox = $checkedRadio.siblings('input[type="text"]'); 
    saveableData = $checkedRadio.val(); 
    if ($textBox.length > 0) 
        saveableData += ',' + $textBox.val(); 
    return saveableData; 

Related links


There are no related external articles for this article.