Skip to content

Commit

Permalink
Add setValueIf and setValueExpression into question fix #7076 (#7077)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewtelnov committed Oct 5, 2023
1 parent f6af288 commit 87c19f7
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 22 deletions.
86 changes: 67 additions & 19 deletions src/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ export interface IQuestionPlainData {
[key: string]: any;
}

class TriggerExpressionInfo {
runner: ExpressionRunner;
isRunning: boolean;
constructor(public name: string, public canRun: () => boolean, public doComplete: () => void) {}
}

/**
* A base class for all questions.
*/
Expand Down Expand Up @@ -131,6 +137,11 @@ export class Question extends SurveyElement<Question>
};
this.locTitle.storeDefaultText = true;
this.createLocalizableString("requiredErrorText", this);
this.addTriggerInfo("resetValueIf", (): boolean => !this.isEmpty(), (): void => {
this.clearValue();
this.updateValueWithDefaults();
});
this.addTriggerInfo("setValueIf", (): boolean => true, (): void => this.runSetValueExpression());
this.registerPropertyChangedHandlers(["width"], () => {
this.updateQuestionCss();
if (!!this.parent) {
Expand Down Expand Up @@ -508,28 +519,54 @@ export class Question extends SurveyElement<Question>
requiredAnsweredQuestionCount: !this.isEmpty() && this.isRequired ? 1 : 0,
};
}
private resetValueIfExpression: ExpressionRunner;
private isRunningResetValueIf: boolean;
public runTriggers(name: string, value: any): void {
if(this.isRunningResetValueIf || this.isReadOnly || !this.resetValueIf || this.isEmpty() || this.isSettingQuestionValue) return;
if(this.parentQuestion && this.parentQuestion.getValueName() === name) return;
if(!this.resetValueIfExpression) {
this.resetValueIfExpression = new ExpressionRunner(this.resetValueIf);
this.resetValueIfExpression.onRunComplete = (res: any): void => {
this.isRunningResetValueIf = false;
private setValueExpressionRunner: ExpressionRunner;
private runSetValueExpression(): void {
if(!this.setValueExpression) {
this.clearValue();
} else {
if(!this.setValueExpressionRunner) {
this.setValueExpressionRunner = new ExpressionRunner(this.setValueExpression);
this.setValueExpressionRunner.onRunComplete = (res: any): void => {
if(!this.isTwoValueEquals(this.value, res)) {
this.value = res;
}
};
} else {
this.setValueExpressionRunner.expression = this.setValueExpression;
}
this.setValueExpressionRunner.run(this.getDataFilteredValues(), this.getDataFilteredProperties());
}
}
private triggersInfo: Array<TriggerExpressionInfo> = [];
private addTriggerInfo(name: string, canRun: ()=> boolean, doComplete: () => void): void {
this.triggersInfo.push(new TriggerExpressionInfo(name, canRun, doComplete));
}
private runTriggerInfo(info: TriggerExpressionInfo, name: string, value: any): void {
const expression = this[info.name];
if(!expression || info.isRunning || !info.canRun()) return;
if(!info.runner) {
info.runner = new ExpressionRunner(expression);
info.runner.onRunComplete = (res: any): void => {
if(res === true) {
this.clearValue();
this.updateValueWithDefaults();
info.doComplete();
}
info.isRunning = false;
};
} else {
this.resetValueIfExpression.expression = this.resetValueIf;
info.runner.expression = expression;
}
const keys: any = {};
keys[name] = value;
if(!new ProcessValue().isAnyKeyChanged(keys, this.resetValueIfExpression.getVariables())) return;
this.isRunningResetValueIf = true;
this.resetValueIfExpression.run(this.getDataFilteredValues(), this.getDataFilteredProperties());
if(!new ProcessValue().isAnyKeyChanged(keys, info.runner.getVariables())) return;
info.isRunning = true;
info.runner.run(this.getDataFilteredValues(), this.getDataFilteredProperties());
}
public runTriggers(name: string, value: any): void {
if(this.isReadOnly || this.isSettingQuestionValue ||
(this.parentQuestion && this.parentQuestion.getValueName() === name)) return;
this.triggersInfo.forEach(info => {
this.runTriggerInfo(info, name, value);
});
}
private runConditions() {
if (this.data && !this.isLoadingFromJson) {
Expand Down Expand Up @@ -1620,6 +1657,18 @@ export class Question extends SurveyElement<Question>
public set resetValueIf(val: string) {
this.setPropertyValue("resetValueIf", val);
}
public get setValueIf(): string {
return this.getPropertyValue("setValueIf");
}
public set setValueIf(val: string) {
this.setPropertyValue("setValueIf", val);
}
public get setValueExpression(): string {
return this.getPropertyValue("setValueExpression");
}
public set setValueExpression(val: string) {
this.setPropertyValue("setValueExpression", val);
}
public get resizeStyle() {
return this.allowResizeComment ? "both" : "none";
}
Expand Down Expand Up @@ -2605,10 +2654,9 @@ Serializer.addClass("question", [
},
{ name: "valueName", onSettingValue: (obj: any, val: any): any => { return makeNameValid(val); } },
"enableIf:condition",
{
name: "resetValueIf:condition",
category: "logic"
},
"resetValueIf:condition",
{ name: "setValueIf:condition", visible: false },
{ name: "setValueExpression:expression", visibleIf: (obj: any): boolean => { return !!obj.setValueIf; } },
"defaultValue:value",
{
name: "defaultValueExpression:expression",
Expand Down
17 changes: 15 additions & 2 deletions src/question_matrixdropdowncolumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,18 @@ export class MatrixDropdownColumn extends Base
public set defaultValueExpression(val: string) {
this.templateQuestion.defaultValueExpression = val;
}
public get setValueIf(): string {
return this.templateQuestion.setValueIf;
}
public set setValueIf(val: string) {
this.templateQuestion.setValueIf = val;
}
public get setValueExpession(): string {
return this.templateQuestion.setValueExpession;
}
public set setValueExpession(val: string) {
this.templateQuestion.setValueExpession = val;
}
public get isUnique(): boolean {
return this.getPropertyValue("isUnique");
}
Expand Down Expand Up @@ -739,11 +751,12 @@ Serializer.addClass(
"enableIf:condition",
"requiredIf:condition",
"resetValueIf:condition",
{ name: "setValueIf:condition", visible: false },
{ name: "setValueExpression:expression", visibleIf: (obj: any): boolean => { return !!obj.setValueIf; } },
{
name: "showInMultipleColumns:boolean",
dependsOn: "cellType",
visibleIf: function (obj: any) {
if (!obj) return false;
visibleIf: (obj: any): boolean => {
return obj.isSupportMultipleColumns;
},
},
Expand Down
37 changes: 37 additions & 0 deletions tests/question_matrixdropdownbasetests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,43 @@ QUnit.test("question.resetValueIf, cycle calls", function (assert) {
assert.equal(q3.isEmpty(), true, "q2.value #3");
assert.equal(q3.isEmpty(), true, "q3.value #3");
});
QUnit.test("question.setValueIf, basic functionality", function (assert) {
const survey = new SurveyModel({
elements: [
{
type: "matrixdynamic",
name: "matrix",
rowCount: 1,
columns: [
{ name: "q1", cellType: "text" },
{ name: "q2", cellType: "text", setValueIf: "{row.q1} = 1", setValueExpression: "{row.q1} + {row.q3}" },
{ name: "q3", cellType: "text" },
]
}
]
});
const matrix = survey.getQuestionByName("matrix");
const row = matrix.visibleRows[0];
const q1 = row.getQuestionByName("q1");
const q2 = row.getQuestionByName("q2");
const q3 = row.getQuestionByName("q3");
const col2 = matrix.getColumnByName("q2");
assert.equal(col2.setValueIf, "{row.q1} = 1", "Load from JSON, column.setValueIf");
assert.equal(q2.setValueIf, "{row.q1} = 1", "Load from JSON, question.setValueIf");
assert.equal(col2.setValueExpression, "{row.q1} + {row.q3}", "Load from JSON, column.setValueExpression");
assert.equal(q2.setValueExpression, "{row.q1} + {row.q3}", "Load from JSON, question.setValueExpression");
q2.value = "abc";
q1.value = 2;
q3.value = 3;
assert.equal(q2.value, "abc", "value is set");
q1.value = 1;
assert.equal(q2.value, 4, "value is set correctly");
q2.value = "edf";
assert.equal(q2.value, "edf", "value is set, #2");
q3.value = 5;
assert.equal(q2.value, "edf", "value is set, #3");
});

QUnit.test("question.onHidingContent", function (assert) {
const survey = new SurveyModel({
questionErrorLocation: "bottom",
Expand Down
34 changes: 33 additions & 1 deletion tests/question_paneldynamic_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6394,4 +6394,36 @@ QUnit.test("question.resetValueIf based on root and row questions", function (as
assert.equal(q2.value, "edf", "q2.value, #4");
q1.value = 1;
assert.equal(q2.isEmpty(), true, "q2.value #5");
});
});
QUnit.test("question.setValueIf, basic functionality", function (assert) {
const survey = new SurveyModel({
elements: [
{
type: "paneldynamic",
name: "panel",
panelCount: 1,
templateElements: [
{ name: "q1", type: "text" },
{ name: "q2", type: "text", setValueIf: "{panel.q1} = 1", setValueExpression: "{panel.q1} + {panel.q3}" },
{ name: "q3", type: "text" }
]
}
]
});
const panel = survey.getQuestionByName("panel");
const q1 = panel.panels[0].getQuestionByName("q1");
const q2 = panel.panels[0].getQuestionByName("q2");
const q3 = panel.panels[0].getQuestionByName("q3");
assert.equal(q2.setValueIf, "{panel.q1} = 1", "Load from JSON setValueIf");
assert.equal(q2.setValueExpression, "{panel.q1} + {panel.q3}", "Load from JSON setValueExpression");
q2.value = "abc";
q1.value = 2;
q3.value = 3;
assert.equal(q2.value, "abc", "value is set");
q1.value = 1;
assert.equal(q2.value, 4, "value is set correctly");
q2.value = "edf";
assert.equal(q2.value, "edf", "value is set, #2");
q3.value = 5;
assert.equal(q2.value, "edf", "value is stay, #3");
});
33 changes: 33 additions & 0 deletions tests/surveyquestiontests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7345,6 +7345,39 @@ QUnit.test("question.resetValueIf and invisibleQuestions", function (assert) {
q1.value = 1;
assert.equal(q2.isEmpty(), true, "value is cleared");
});
QUnit.test("question.setValueIf, basic functionality", function (assert) {
const survey = new SurveyModel({
elements: [{
"name": "q1",
"type": "text"
},
{
"name": "q2",
"type": "text",
"setValueIf": "{q1} = 1",
"setValueExpression": "{q1} + {q3}"
},
{
"name": "q3",
"type": "text"
}
] });
const q1 = survey.getQuestionByName("q1");
const q2 = survey.getQuestionByName("q2");
const q3 = survey.getQuestionByName("q3");
assert.equal(q2.setValueIf, "{q1} = 1", "Load from JSON, setValueIf");
assert.equal(q2.setValueExpression, "{q1} + {q3}", "Load from JSON, setValueExpression");
q2.value = "abc";
q1.value = 2;
q3.value = 3;
assert.equal(q2.value, "abc", "value is set");
q1.value = 1;
assert.equal(q2.value, 4, "value is set");
q2.value = "edf";
assert.equal(q2.value, "edf", "value is set, #2");
q3.value = 5;
assert.equal(q2.value, "edf", "value is stay, #3");
});

QUnit.test("question.isReady & async functions in expression", function (assert) {
var returnResult1: (res: any) => void;
Expand Down

0 comments on commit 87c19f7

Please sign in to comment.