4

I am trying to validate the input based on previous selection using SurveyJS. The input in last section of Duration should depend on previous selection of Days or Months or Years.

If I keep only condition in validation it works fine but when I add more than 1 condition it is trying to validate against all 3 irrespective of expression condition.

Below JSON can be used in Survey JS codepen or codesandbox:

{
  "pages": [
    {
      "name": "page1",
      "elements": [
        {
          "type": "radiogroup",
          "name": "Gym",
          "title": "Do you go to GYM",
          "isRequired": true,
          "choices": ["Yes", "No"]
        },
        {
          "type": "radiogroup",
          "name": "GymDur",
          "visibleIf": "{Gym} = 'Yes'",
          "title": "How long have you been going to GYM",
          "resetValueIf": "{Gym} = 'No'",
          "isRequired": true,
          "choices": ["Days", "Months", "Years"]
        },
        {
          "name": "GymDura",
          "type": "text",
          "title": "Enter Duration",
          "inputType": "number",
          validators: [
            {
              type: "regex",
              regex: "^(?:[0-9]|[1-2][0-9])$",
              text: "Value should be less than 29.99",
              expression: "{GymDur} = 'Days'",
            },
            {
              type: "regex",
              regex: "^(?:[0-9]|1[01])$",
              text: "Value should be less than 11.99",
              expression: "{GymDur} ='Months'",
            },
            {
              type: "regex",
              regex: "^(?:d{1,2})(?:.d{1,2})?$",
              text: "Value should be less than 99.99",
              expression: "{GymDur} ='Years'",
            },
          ],
        },
      ],
    }
  ],
}

Is there any way we can have validators work based on the single expression inputs (Days, Months and Years) only and if I change the value if Input (Days to Months or Year) the data and validation error in Enter Duration should automatically get reset. Below is the screenshot for same. enter image description here

1 Answer 1

2

If an ExpressionValidator can be used instead of the regex validator (and for the particular example in the question it can), a solution is to define a "dynamic validator", i.e., one that contains calculated values.

For the problem in the OP, we can set the GymDura question as:

{
   name: "GymDura",
   type: "text",
   title: "Enter Duration",
   inputType: "number",
   validators: [
      {
         type: "expression",
         expression: "{GymDura} < {maxValue}",
         text: "Value should be less than {maxValue}",
      }
   ]
}

(I use javascript/JSON5 for readability)

The validator is defined by the expression "{GymDura} < {maxValue}" that contains the calculated value maxValue, which also appears in the text of the validator.

That calculated value can be simply defined using iif function as

calculatedValues: [
   {
      name: "maxValue", 
      expression: "iif({GymDur} = 'Days', 29.99, iif({GymDur} = 'Months', 11.99, 99.99)"
   },
]

Here's a demo snippet:

const json = {
   checkErrorsMode: "onValueChanged",
   pages: [
      {
         name: "page1",
         elements: [
            {
               type: "radiogroup",
               name: "Gym",
               title: "Do you go to GYM",
               isRequired: true,
               choices: ["Yes", "No"],
            },
            {
               type: "radiogroup",
               name: "GymDur",
               visibleIf: "{Gym} = 'Yes'",
               title: "How long have you been going to GYM",
               resetValueIf: "{Gym} = 'No'",
               isRequired: true,
               choices: ["Days", "Months", "Years"],
            },
            {
               name: "GymDura",
               type: "text",
               title: "Enter Duration",
               inputType: "number",
               validators: [
                  {
                     type: "expression",
                     expression: "{GymDura} < {maxValue}",
                     text: "Value should be less than {maxValue}",
                  }
               ],
            },
         ]
      },
   ],
   calculatedValues: [
      {name: "maxValue", expression: "iif({GymDur} = 'Days', 29.99, iif({GymDur} = 'Months', 11.99, 99.99)"},
   ],
};

const survey = new Survey.Model(json);
survey.onComplete.add((sender, options) => {
   console.log(JSON.stringify(sender.data, null, 3));
});
survey.render(document.getElementById("surveyElement"));
<div id="surveyElement" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; min-height: 100%; height:100%"></div>

<link href="https://unpkg.com/survey-core/survey-core.min.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="https://unpkg.com/survey-core/survey.core.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/survey-js-ui/survey-js-ui.min.js"></script>


If a regex validator has to be used, the regex property is not dynamic, so a code based solution can be devised.

One can acces the .validators property of the question, which is a writable array-like object and can be manipulated through a few of the array methods it implements. It can't however be simply assigned to a new array, as the array and the validators it contains will not be recognized by surveyjs so the question will have no validators at all.

One can thus add to SurveyComponent constructor in React, or the main scope of the script in VanillaJS (see runnable snippet below).

function SurveyComponent(){
  // ...... original code 
  
  const page = survey.getPageByName("page1");
  const qGymDur = page.getQuestionByName("GymDur");
  const qGymDura = page.getQuestionByName("GymDura");

  const allValidators = [...qGymDura.validators]; // store all validators
  qGymDura.validators.splice(0, 3); // remove all validators from the question
  qGymDur.onPropertyChanged.add((_, options) => {
    if (options.name === "value") {
      page.clearErrors(); // clear previous errors (if any)
      for (let i = 0; i < qGymDur.choices.length; i++) {
        if (options.newValue === qGymDur.choices[i].value) {
          qGymDura.validators.pop(); // clear previous validator (if any)
          qGymDura.validators.push(allValidators[i]);
          break;
        }
      }
    }
  });
}

The method PageModel#clearErrors was used to remove errors when a new selection was made.

Runnable snippet:

const json = {
   pages: [
      {
         name: "page1",
         elements: [
            {
               type: "radiogroup",
               name: "Gym",
               title: "Do you go to GYM",
               isRequired: true,
               choices: ["Yes", "No"],
            },
            {
               type: "radiogroup",
               name: "GymDur",
               visibleIf: "{Gym} = 'Yes'",
               title: "How long have you been going to GYM",
               resetValueIf: "{Gym} = 'No'",
               isRequired: true,
               choices: ["Days", "Months", "Years"],
            },
            {
               name: "GymDura",
               type: "text",
               title: "Enter Duration",
               inputType: "number",
               validators: [
                  {
                     type: "regex",
                     regex: "^(?:[0-9]|[1-2][0-9])$",
                     text: "Value should be less than 29.99",
                  },
                  {
                     type: "regex",
                     regex: "^(?:[0-9]|1[01])$",
                     text: "Value should be less than 11.99",
                  },
                  {
                     type: "regex",
                     regex: "^(?:d{1,2})(?:.d{1,2})?$",
                     text: "Value should be less than 99.99",
                  },
               ],
            },
         ],
      },
   ],
};
const survey = new Survey.Model(json);
survey.onComplete.add((sender, options) => {
   console.log(JSON.stringify(sender.data, null, 3));
});
survey.render(document.getElementById("surveyElement"));

const page = survey.getPageByName("page1");
const qGymDur = page.getQuestionByName("GymDur");
const qGymDura = page.getQuestionByName("GymDura");

const allValidators = [...qGymDura.validators]; // store all validators
qGymDura.validators.splice(0, 3); // remove all validators from the question
qGymDur.onPropertyChanged.add((_, options) => {
   if (options.name === "value") {
      page.clearErrors(); // clear previous errors (if any)
      for (let i = 0; i < qGymDur.choices.length; i++) {
         if (options.newValue === qGymDur.choices[i].value) {
            qGymDura.validators.pop(); // clear previous validator (if any)
            qGymDura.validators.push(allValidators[i]);
            break;
         }
      }
   }
});
<div id="surveyElement" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; min-height: 100%; height:100%"></div>

<link href="https://unpkg.com/survey-core/survey-core.min.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="https://unpkg.com/survey-core/survey.core.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/survey-js-ui/survey-js-ui.min.js"></script>

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.