Skip to content

Commit

Permalink
Multi-Select Matrix - Cell values are not updated when using defaultV… (
Browse files Browse the repository at this point in the history
#8345)

* Multi-Select Matrix - Cell values are not updated when using defaultValueExpression fix #8339

* Do not store default names on matrix disposing #8339
  • Loading branch information
andrewtelnov authored Jun 3, 2024
1 parent 89893ea commit 8ef172a
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 11 deletions.
7 changes: 5 additions & 2 deletions src/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1858,6 +1858,9 @@ export class Question extends SurveyElement<Question>
if (this.isDesignMode && this.isContentElement && this.isDefaultValueEmpty()) return;
this.setDefaultValue();
}
public get isValueDefault(): boolean {
return !this.isEmpty() && (this.isTwoValueEquals(this.defaultValue, this.value) || !this.isValueChangedDirectly && !!this.defaultValueExpression);
}
protected get isClearValueOnHidden(): boolean {
const clearIf = this.getClearIfInvisible();
if (clearIf === "none" || clearIf === "onComplete") return false;
Expand Down Expand Up @@ -1931,8 +1934,8 @@ export class Question extends SurveyElement<Question>
});
};
}
if (!values) values = this.data.getFilteredValues();
if (!properties) properties = this.data.getFilteredProperties();
if (!values) values = this.defaultValueExpression ? this.data.getFilteredValues() : {};
if (!properties) properties = this.defaultValueExpression ? this.data.getFilteredProperties() : {};
if (!!runner && runner.canRun) {
runner.onRunComplete = (res) => {
if (res == undefined) res = this.defaultValue;
Expand Down
25 changes: 23 additions & 2 deletions src/question_matrixdropdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,36 @@ export class QuestionMatrixDropdownModel extends QuestionMatrixDropdownModelBase
super.clearValueIfInvisibleCore(reason);
this.clearInvisibleValuesInRows();
}
private defaultValuesInRows: any = {};
protected clearGeneratedRows(): void {
if (!this.generatedVisibleRows) return;
if(!this.isDisposed) {
this.generatedVisibleRows.forEach(row => {
this.defaultValuesInRows[row.rowName] = row.getNamesWithDefaultValues();
});
}
super.clearGeneratedRows();
}
private getRowValueForCreation(val: any, rowValue: any): any {
const res = val[rowValue];
if(!res) return res;
const names = this.defaultValuesInRows[rowValue];
if(!Array.isArray(names) || names.length === 0) return res;
names.forEach(name => {
delete res[name];
});
return res;
}
protected generateRows(): Array<MatrixDropdownRowModel> {
var result = new Array<MatrixDropdownRowModel>();
var rows = !!this.filteredRows ? this.filteredRows : this.rows;
if (!rows || rows.length === 0) return result;
var val = this.value;
if (!val) val = {};
for (var i = 0; i < rows.length; i++) {
if (this.isValueEmpty(rows[i].value)) continue;
result.push(this.createMatrixRow(rows[i], val[rows[i].value]));
const row = rows[i];
if (this.isValueEmpty(row.value)) continue;
result.push(this.createMatrixRow(row, this.getRowValueForCreation(val, row.value)));
}
return result;
}
Expand Down
19 changes: 16 additions & 3 deletions src/question_matrixdropdownbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,18 +385,22 @@ export class MatrixDropdownRowModelBase implements ISurveyData, ISurveyImpl, ILo
}
}
res.row = this.getAllValues();
this.applyRowVariablesToValues(res, this.rowIndex);
return res;
}
getFilteredProperties(): any {
return { survey: this.getSurvey(), row: this };
}
private applyRowVariablesToValues(res: any, rowIndex: number): void {
res[MatrixDropdownRowModelBase.IndexVariableName] = rowIndex;
res[MatrixDropdownRowModelBase.RowValueVariableName] = this.rowName;
}
public runCondition(values: HashTable<any>, properties: HashTable<any>): void {
if (!!this.data) {
values[MatrixDropdownRowModelBase.OwnerVariableName] = this.data.value;
}
const rowIndex = this.rowIndex;
values[MatrixDropdownRowModelBase.IndexVariableName] = rowIndex;
values[MatrixDropdownRowModelBase.RowValueVariableName] = this.rowName;
this.applyRowVariablesToValues(values, rowIndex);
const newProps = Helpers.createCopy(properties);
newProps[MatrixDropdownRowModelBase.RowVariableName] = this;
const rowValues = rowIndex > 0 ? this.data.getRowValue(this.rowIndex - 1) : this.value;
Expand All @@ -411,6 +415,15 @@ export class MatrixDropdownRowModelBase implements ISurveyData, ISurveyImpl, ILo
this.detailPanel.runCondition(values, newProps);
}
}
public getNamesWithDefaultValues(): Array<string> {
const res: Array<string> = [];
this.questions.forEach(q => {
if(q.isValueDefault) {
res.push(q.getValueName());
}
});
return res;
}
public clearValue(keepComment?: boolean): void {
var questions = this.questions;
for (var i = 0; i < questions.length; i++) {
Expand Down Expand Up @@ -1154,7 +1167,7 @@ export class QuestionMatrixDropdownModelBase extends QuestionMatrixBaseModel<Mat
this.renderedTableValue = null;
this.fireCallback(this.onRenderedTableResetCallback);
}
protected clearGeneratedRows() {
protected clearGeneratedRows(): void {
if (!this.generatedVisibleRows) return;
for (var i = 0; i < this.generatedVisibleRows.length; i++) {
this.generatedVisibleRows[i].dispose();
Expand Down
4 changes: 0 additions & 4 deletions src/question_matrixdynamic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import {
MatrixDropdownRowModelBase,
IMatrixDropdownData
} from "./question_matrixdropdownbase";
import { surveyLocalization } from "./surveyStrings";
import { LocalizableString } from "./localizablestring";
import { SurveyError } from "./survey-error";
import { MinRowCountError } from "./error";
import { IAction } from "./actions/action";
Expand All @@ -17,9 +15,7 @@ import { DragDropMatrixRows } from "./dragdrop/matrix-rows";
import { IShortcutText, ISurveyImpl, IProgressInfo } from "./base-interfaces";
import { CssClassBuilder } from "./utils/cssClassBuilder";
import { QuestionMatrixDropdownRenderedTable } from "./question_matrixdropdownrendered";
import { MatrixDropdownColumn } from "./question_matrixdropdowncolumn";
import { DragOrClickHelper } from "./utils/dragOrClickHelper";
import { Helpers } from "./helpers";

export class MatrixDynamicRowModel extends MatrixDropdownRowModelBase implements IShortcutText {
private dragOrClickHelper: DragOrClickHelper;
Expand Down
117 changes: 117 additions & 0 deletions tests/question_matrixdropdownbasetests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1228,3 +1228,120 @@ QUnit.test("column.visibleIf", function (assert) {
assert.equal(row1Cell2.isVisible, true, "row1Cell2.isVisible #3");
assert.equal(row2Cell2.isVisible, true, "row2Cell2.isVisible #3");
});
QUnit.test("defaultValueExpression & regeneration of rows", function (assert) {
const survey = new SurveyModel({
elements: [
{
type: "text",
name: "q1",
defaultValue: 5
},
{
type: "matrixdynamic",
name: "matrix",
rowCount: 1,
columns: [
{
name: "col1",
cellType: "text",
defaultValueExpression: "{q1}"
}
]
}
]
});
const matrix = <QuestionMatrixDynamicModel>survey.getQuestionByName("matrix");
const question = survey.getQuestionByName("q1");
assert.equal(matrix.visibleRows[0].cells[0].question.value, 5, "question value #1");
question.value = 10;
const id = matrix.visibleRows[0].id;
assert.equal(matrix.visibleRows[0].cells[0].question.value, 10, "question value #2");
matrix.addColumn("col2");
assert.notEqual(matrix.visibleRows[0].id, id, "It is a new row");
question.value = 15;
assert.equal(matrix.visibleRows[0].cells[0].question.value, 15, "question value #3");
});
QUnit.test("defaultValueExpression & using rowvalue in it", function (assert) {
const survey = new SurveyModel({
elements: [
{
type: "text",
name: "q1",
defaultValue: 5
},
{
type: "matrixdynamic",
name: "matrix",
rowCount: 1,
columns: [
{
name: "col1",
cellType: "text",
defaultValueExpression: "{q1}"
}
]
}
]
});
const matrix = <QuestionMatrixDynamicModel>survey.getQuestionByName("matrix");
const question = survey.getQuestionByName("q1");
assert.equal(matrix.visibleRows[0].cells[0].question.value, 5, "question value #1");
question.value = 10;
const id = matrix.visibleRows[0].id;
assert.equal(matrix.visibleRows[0].cells[0].question.value, 10, "question value #2");
matrix.addColumn("col2");
assert.notEqual(matrix.visibleRows[0].id, id, "It is a new row");
question.value = 15;
assert.equal(matrix.visibleRows[0].cells[0].question.value, 15, "question value #3");
});
QUnit.test("defaultValueExpression & using rowvalue in it", function (assert) {
const survey = new SurveyModel({
elements: [
{
type: "text",
name: "q1",
defaultValue: 5
},
{
type: "text",
name: "q2",
defaultValue: 10
},
{
type: "matrixdropdown",
name: "matrix",
columns: [
{
name: "col1",
cellType: "text",
defaultValueExpression: "iif({rowvalue} = 'row1', {q1}, iif({rowvalue} = 'row2', {q2}))"
},
{ name: "col2", cellType: "text" }
],
rows: [
{ value: "row1", visibleIf: "{q1} > 1" },
{ value: "row2", visibleIf: "{q2} > 1" }]
}
]
});
const matrix = <QuestionMatrixDynamicModel>survey.getQuestionByName("matrix");
const q1 = survey.getQuestionByName("q1");
const q2 = survey.getQuestionByName("q2");
assert.equal(matrix.visibleRows[0].cells[0].question.value, 5, "cell1 value #1");
assert.equal(matrix.visibleRows[1].cells[0].question.value, 10, "cell1 value #1");
matrix.visibleRows[0].cells[1].question.value = 12;
q1.value = 15;
q2.value = 20;
assert.equal(matrix.visibleRows[0].cells[0].question.value, 15, "cell1 value #2");
assert.equal(matrix.visibleRows[1].cells[0].question.value, 20, "cell1 value #2");
q1.value = 0;
assert.equal(matrix.visibleRows.length, 1, "visible rows count #1");
const id = matrix.visibleRows[0].id;
q1.value = 30;
q2.value = 50;
assert.notEqual(matrix.visibleRows[0].id, id, "It is a new row");
assert.equal(matrix.visibleRows.length, 2, "visible rows count #2");
assert.equal(matrix.visibleRows[0].cells[1].question.value, 12, "Keep value in the standard cell");
assert.equal(matrix.visibleRows[0].cells[0].question.value, 30, "cell1 value #3");
assert.equal(matrix.visibleRows[1].cells[0].question.value, 50, "cell1 value #3");
});
34 changes: 34 additions & 0 deletions tests/surveyquestiontests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7781,3 +7781,37 @@ QUnit.test("QuestionImagePickerModel.needResponsiveWidth", function (assert) {
q.colCount = 3;
assert.equal(q.needResponsiveWidth(), true, "Responsive in auto mode for several columns");
});
QUnit.test("question.isDefaultValue", function (assert) {
const survey = new SurveyModel({
elements: [
{ name: "q1", type: "text", defaultValue: 1 },
{ name: "q2", type: "text", defaultValueExpression: "1 + 1" },
{ name: "q3", type: "text", defaultValueExpression: "{q1} + {q2}" },
{ name: "q4", type: "text" }
]
});
const q1 = survey.getQuestionByName("q1");
const q2 = survey.getQuestionByName("q2");
const q3 = survey.getQuestionByName("q3");
const q4 = survey.getQuestionByName("q4");
assert.equal(q1.value, 1, "q1 value is 1");
assert.equal(q1.isValueDefault, true, "q1 #1");
assert.equal(q2.isValueDefault, true, "q2 #1");
assert.equal(q3.isValueDefault, true, "q3 #1");
assert.equal(q4.isValueDefault, false, "q4 #1");
q1.value = "";
assert.equal(q1.isValueDefault, false, "q1 #2");
assert.equal(q2.isValueDefault, true, "q2 #2");
assert.equal(q3.isValueDefault, true, "q3 #2");
assert.equal(q4.isValueDefault, false, "q4 #2");
q2.value = 4;
assert.equal(q1.isValueDefault, false, "q1 #3");
assert.equal(q2.isValueDefault, false, "q2 #3");
assert.equal(q3.isValueDefault, true, "q3 #3");
assert.equal(q4.isValueDefault, false, "q4 #3");
q3.value = 10;
assert.equal(q1.isValueDefault, false, "q1 #4");
assert.equal(q2.isValueDefault, false, "q2 #4");
assert.equal(q3.isValueDefault, false, "q3 #4");
assert.equal(q4.isValueDefault, false, "q4 #4");
});

0 comments on commit 8ef172a

Please sign in to comment.