Skip to content

Commit

Permalink
Add validate callback function into Components definition fix #8035 (#…
Browse files Browse the repository at this point in the history
…8036)

* Add validate callback function into Components definition fix #8035

* Add a description

* Rename the callback function #8035

---------

Co-authored-by: RomanTsukanov <sergeich16@gmail.com>
  • Loading branch information
andrewtelnov and RomanTsukanov committed Apr 2, 2024
1 parent 4a4e876 commit 028c705
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/question_custom.ts
Expand Up @@ -18,6 +18,8 @@ import { ItemValue } from "./itemvalue";
import { QuestionTextProcessor } from "./textPreProcessor";
import { CssClassBuilder } from "./utils/cssClassBuilder";
import { LocalizableString } from "./localizablestring";
import { SurveyError } from "./survey-error";
import { CustomError } from "./error";

/**
* An interface used to create custom question types.
Expand Down Expand Up @@ -243,6 +245,12 @@ export interface ICustomQuestionTypeConfiguration {
* @see questionJSON
*/
createQuestion?: any;
/**
* A function that allows you to display different error texts based on conditions.
* @param question A custom question. Use the `question.value` property to access the question's value.
* @returns An error text.
*/
getErrorText?: (question: Question) => string;
valueToQuestion?: (val: any) => any;
valueFromQuestion?: (val: any) => any;
getValue?: (val: any) => any;
Expand Down Expand Up @@ -310,6 +318,10 @@ export class ComponentQuestionJSON {
if (!this.json.onValueChanging) return newValue;
return this.json.onValueChanging(question, name, newValue);
}
public onGetErrorText(question: Question): string {
if (!this.json.getErrorText) return undefined;
return this.json.getErrorText(question);
}
public onItemValuePropertyChanged(
question: Question,
item: ItemValue,
Expand Down Expand Up @@ -599,6 +611,15 @@ export abstract class QuestionCustomModelBase extends Question
super.setNewValue(newValue);
this.updateElementCss();
}
protected onCheckForErrors(errors: Array<SurveyError>, isOnValueChanged: boolean): void {
super.onCheckForErrors(errors, isOnValueChanged);
if (!!this.customQuestion) {
const text = this.customQuestion.onGetErrorText(this);
if(!!text) {
errors.push(new CustomError(text, this));
}
}
}
//ISurveyImpl
getSurveyData(): ISurveyData {
return this;
Expand Down
77 changes: 77 additions & 0 deletions tests/question_customtests.ts
Expand Up @@ -3031,4 +3031,81 @@ QUnit.test("Single: showPreviewBeforeComplete Bug#8005", function (assert) {
survey.completeLastPage();
assert.deepEqual(survey.data, { question1: 1 }, "survey.data #2");
ComponentCollection.Instance.clear();
});
QUnit.test("Single: validate", function (assert) {
let errorText = "";
ComponentCollection.Instance.add({
name: "test",
questionJSON: { type: "dropdown", choices: [1, 2, 3] },
getErrorText: (question): string => {
if(question.value !== 1) {
errorText = "val";
return "value should be 1";
}
return "";
}
});
const survey = new SurveyModel({
elements: [{ type: "test", name: "question1" }],
showPreviewBeforeComplete: "showAllQuestions"
});
const q = survey.getQuestionByName("question1");
q.value = 2;
survey.validate();
assert.equal(errorText, "val", "errorText");
assert.equal(q.errors.length, 1, "Errors length #1");
assert.equal(q.errors[0].text, "value should be 1", "Error text");
q.value = 1;
assert.equal(q.errors.length, 0, "Errors length #2");
ComponentCollection.Instance.clear();
});
QUnit.test("Composite: validate", function (assert) {
ComponentCollection.Instance.add({
name: "test",
elementsJSON: [
{ type: "text", name: "q1" },
{ type: "dropdown", name: "q2", choices: [1, 2, 3], visibleIf: "{composite.q1} notempty" },
{ type: "text", name: "q3", choices: [1, 2, 3], visibleIf: "{composite.q2} notempty" }
],
onValueChanged(question, name, newValue) {
if (name === "q1") {
question.contentPanel.getQuestionByName("q2").clearValue();
}
if (name === "q2") {
question.contentPanel.getQuestionByName("q3").value = newValue;
}
},
getErrorText: (question): string => {
const q1 = question.contentPanel.getQuestionByName("q1");
const q3 = question.contentPanel.getQuestionByName("q3");
if(!q1.isEmpty() && q3.isEmpty()) return "Select q2";
return "";
}
});
const survey = new SurveyModel({
elements: [
{ type: "test", name: "q1" },
{ type: "test", name: "q2", isRequired: true }
]
});
const q1 = <QuestionCompositeModel>survey.getQuestionByName("q1");
const q2 = <QuestionCompositeModel>survey.getQuestionByName("q2");
survey.validate();
assert.equal(q1.errors.length, 0, "q1 errors #1");
assert.equal(q2.errors.length, 1, "q2 errors #1");
q1.contentPanel.getQuestionByName("q1").value = "val";
q2.contentPanel.getQuestionByName("q1").value = "val";
survey.validate();
assert.equal(q1.errors.length, 1, "q1 errors #2");
assert.equal(q1.errors[0].text, "Select q2", "q1 errors text #2");
assert.equal(q2.errors.length, 1, "q2 errors #2");
assert.equal(q2.errors[0].text, "Select q2", "q2 errors text #2");
q1.contentPanel.getQuestionByName("q2").value = 1;
q2.contentPanel.getQuestionByName("q2").value = 2;
assert.equal(q1.contentPanel.getQuestionByName("q3").value, 1, "q1.q3 value");
assert.equal(q2.contentPanel.getQuestionByName("q3").value, 2, "q2.q3 value");
survey.validate();
assert.equal(q1.errors.length, 0, "q1 errors #3");
assert.equal(q2.errors.length, 0, "q2 errors #3");
ComponentCollection.Instance.clear();
});

0 comments on commit 028c705

Please sign in to comment.