From 5a9f7bcd7b16a7f46d623cdba66983ce0a06bb8c Mon Sep 17 00:00:00 2001 From: Andrew Telnov Date: Sun, 27 Jun 2021 21:35:40 +0300 Subject: [PATCH] Fix: (React) Single matrix columns are not updated on changing if matrix is editing in property grid #3027 --- src/base.ts | 5 ++-- src/question_matrix.ts | 32 +++++++++++++++----- tests/editingObjectTests.ts | 60 +++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 10 deletions(-) diff --git a/src/base.ts b/src/base.ts index 4621a32625..4fc7bf1dc8 100644 --- a/src/base.ts +++ b/src/base.ts @@ -412,8 +412,8 @@ export class Base { public static isSurveyElement(val: any): boolean { if (!val) return false; if (Array.isArray(val)) { - if (val.length == 0) return false; - return Base.isSurveyElement(val[0]); + if (!!(val).onArrayChanged) return true; + return val.length > 0 && Base.isSurveyElement(val[0]); } return !!val.getType && !!val.onPropertyChanged; } @@ -646,6 +646,7 @@ export class Base { public iteratePropertiesHash(func: (hash: any, key: any) => void) { var keys: any[] = []; for (var key in this.propertyHash) { + if (key === "value" && Base.isSurveyElement((this).value)) continue; keys.push(key); } keys.forEach((key) => func(this.propertyHash, key)); diff --git a/src/question_matrix.ts b/src/question_matrix.ts index 855dcac1da..fe7700b5cb 100644 --- a/src/question_matrix.ts +++ b/src/question_matrix.ts @@ -264,6 +264,11 @@ export class QuestionMatrixModel getColumns(): Array { return this.visibleColumns; } + public addColumn(value: any, text?: string): ItemValue { + var col = new ItemValue(value, text); + this.columns.push(col); + return col; + } public getItemClass(row: any, column: any) { var isChecked = row.value == column.value; var isDisabled = this.isReadOnly; @@ -484,7 +489,7 @@ export class QuestionMatrixModel if (!!questionPlainData) { var values = this.createValueCopy(); questionPlainData.isNode = true; - questionPlainData.data = Object.keys(values || {}).map(rowName => { + questionPlainData.data = Object.keys(values || {}).map((rowName) => { var row = this.rows.filter( (r: MatrixRowModel) => r.value === rowName )[0]; @@ -505,7 +510,7 @@ export class QuestionMatrixModel values[rowName] ); if (!!item) { - (options.calculations || []).forEach(calculation => { + (options.calculations || []).forEach((calculation) => { rowDataItem[calculation.propertyName] = item[calculation.propertyName]; }); @@ -598,18 +603,29 @@ export class QuestionMatrixModel return this.survey as SurveyModel; } public getColumnHeaderWrapperComponentName(cell: ItemValue) { - return this.SurveyModel.getElementWrapperComponentName({ column: cell }, "column-header"); + return this.SurveyModel.getElementWrapperComponentName( + { column: cell }, + "column-header" + ); } public getColumnHeaderWrapperComponentData(cell: ItemValue) { - return this.SurveyModel.getElementWrapperComponentData({ column: cell }, "column-header"); + return this.SurveyModel.getElementWrapperComponentData( + { column: cell }, + "column-header" + ); } public getRowHeaderWrapperComponentName(cell: ItemValue) { - return this.SurveyModel.getElementWrapperComponentName({ row: cell }, "row-header"); + return this.SurveyModel.getElementWrapperComponentName( + { row: cell }, + "row-header" + ); } public getRowHeaderWrapperComponentData(cell: ItemValue) { - return this.SurveyModel.getElementWrapperComponentData({ row: cell }, "row-header"); + return this.SurveyModel.getElementWrapperComponentData( + { row: cell }, + "row-header" + ); } - } Serializer.addClass( @@ -642,7 +658,7 @@ Serializer.addClass( "matrixbase" ); -QuestionFactory.Instance.registerQuestion("matrix", name => { +QuestionFactory.Instance.registerQuestion("matrix", (name) => { var q = new QuestionMatrixModel(name); q.rows = QuestionFactory.DefaultRows; q.columns = QuestionFactory.DefaultColums; diff --git a/tests/editingObjectTests.ts b/tests/editingObjectTests.ts index 83164d98bf..aa57ad0284 100644 --- a/tests/editingObjectTests.ts +++ b/tests/editingObjectTests.ts @@ -13,6 +13,7 @@ import { QuestionRatingModel } from "../src/question_rating"; import { Serializer } from "../src/jsonobject"; import { ItemValue } from "../src/itemvalue"; import { QuestionMultipleTextModel } from "../src/question_multipletext"; +import { QuestionMatrixModel } from "../src/question_matrix"; export default QUnit.module("Survey.editingObj Tests"); @@ -1053,3 +1054,62 @@ QUnit.test("Do not set directly array objects", function(assert) { matrix.removeRow(1); assert.equal(editingSurvey.pages.length, 1, "There is one page now"); }); + +QUnit.test("Do not break reactive array in original object", function(assert) { + var question = new QuestionMatrixModel("q1"); + question.addColumn("col1"); + question.addColumn("col2"); + var colCountOnChanged = {}; + var reactiveFunc = (hash: any, key: any): void => { + var val: any = hash[key]; + if (typeof val === "function") { + val = val(); + } + if (Array.isArray(val)) { + val["onArrayChanged"] = (arrayChanges: ArrayChanges) => { + colCountOnChanged[key] = val.length; + }; + } + }; + var survey = new SurveyModel({ + elements: [ + { + type: "matrixdynamic", + name: "columns", + rowCount: 0, + columns: [ + { cellType: "text", name: "value" }, + { cellType: "text", name: "text" }, + ], + }, + ], + }); + question.iteratePropertiesHash((hash: any, key: any) => { + reactiveFunc(hash, key); + }); + survey.editingObj = question; + var matrix = survey.getQuestionByName("columns"); + matrix.onGetValueForNewRowCallBack = ( + sender: QuestionMatrixDynamicModel + ): any => { + var item = new ItemValue("val"); + matrix.value.push(item); + return item; + }; + assert.equal(matrix.rowCount, 2, "There are 2 columns"); + assert.equal(Array.isArray(matrix.value), true, "Value is Array"); + assert.strictEqual(matrix.value, question.columns, "Edting value is correct"); + + matrix.iteratePropertiesHash((hash: any, key: any) => { + reactiveFunc(hash, key); + }); + matrix.addRow(); + matrix.addRow(); + assert.equal(matrix.rowCount, 4, "There are 4 columns"); + assert.equal( + colCountOnChanged["columns"], + 4, + "There are 4 columns in notification" + ); + assert.notOk(colCountOnChanged["value"], "We do not iterate by value"); +});