diff --git a/src/components/_classes/component/Component.js b/src/components/_classes/component/Component.js index c538319540..934be40728 100644 --- a/src/components/_classes/component/Component.js +++ b/src/components/_classes/component/Component.js @@ -2853,8 +2853,8 @@ export default class Component extends Element { /* eslint-disable max-statements */ calculateComponentValue(data, flags, row) { - // Skip value calculation for the component if we don't have entire form data set - if (_.isUndefined(_.get(this, 'root.data'))) { + // Skip value calculation for the component if we don't have entire form data set or in builder mode + if (this.builderMode || _.isUndefined(_.get(this, 'root.data'))) { return false; } // If no calculated value or @@ -2924,6 +2924,7 @@ export default class Component extends Element { // Check to ensure that the calculated value is different than the previously calculated value. if (previousCalculatedValue && previousChanged && !calculationChanged) { + this.calculatedValue = null; return false; } diff --git a/src/components/_classes/nested/NestedComponent.js b/src/components/_classes/nested/NestedComponent.js index dfee7555a1..5bff5686e0 100644 --- a/src/components/_classes/nested/NestedComponent.js +++ b/src/components/_classes/nested/NestedComponent.js @@ -599,8 +599,8 @@ export default class NestedComponent extends Field { row = row || this.data; components = components && _.isArray(components) ? components : this.getComponents(); const isValid = components.reduce((valid, comp) => { - return comp.checkData(data, flags, row) && valid; - }, super.checkData(data, flags, row)); + return comp.checkData(data, { ...flags }, row) && valid; + }, super.checkData(data, { ...flags }, row)); this.checkModal(isValid, this.isDirty); return isValid; diff --git a/src/components/datagrid/DataGrid.js b/src/components/datagrid/DataGrid.js index 667b86055a..7436d80b26 100644 --- a/src/components/datagrid/DataGrid.js +++ b/src/components/datagrid/DataGrid.js @@ -701,7 +701,8 @@ export default class DataGridComponent extends NestedArrayComponent { this.dataValue = value; - if (this.initRows || isSettingSubmission) { + if (this.initRows || isSettingSubmission || + (Array.isArray(this.dataValue) && this.dataValue.length !== this.rows.length)) { if (!this.createRows() && changed) { this.redraw(); } diff --git a/src/components/datagrid/DataGrid.unit.js b/src/components/datagrid/DataGrid.unit.js index e563067abd..d725f1d945 100644 --- a/src/components/datagrid/DataGrid.unit.js +++ b/src/components/datagrid/DataGrid.unit.js @@ -21,7 +21,8 @@ import { withConditionalFieldsAndValidations, withLogic, withCollapsibleRowGroups, - withAllowCalculateOverride + withAllowCalculateOverride, + twoWithAllowCalculatedOverride, } from './fixtures'; describe('DataGrid Component', () => { @@ -512,12 +513,8 @@ describe('DataGrid calculated values', () => { assert.deepEqual(dataGrid.getValue(), [{ - firstName: 'initial 1', - lastName: 'initial 2' - }, - { - firstName: 'initial 1b', - lastName: 'initial 2b' + firstName: '', + lastName: '' }] ); @@ -564,4 +561,148 @@ describe('DataGrid calculated values', () => { }) .catch(done); }); + + it('Should not recalculate value after restoring to previous calculated value', (done) => { + Formio.createForm(document.createElement('div'), withAllowCalculateOverride) + .then((form) => { + const select = form.getComponent('select'); + const dataGrid = form.getComponent('dataGrid'); + + assert.deepEqual(dataGrid.getValue(), + [{ + firstName: '', + lastName: '' + }] + ); + + select.setValue('a', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), + [{ + firstName: 'A f 1', + lastName: 'A l 1' + }] + ); + + const firstName = form.getComponent(['dataGrid', 0, 'firstName']); + firstName.setValue('first name', { modified: true }); + setTimeout(() => { + select.setValue('c', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), + [{ + firstName: 'first name', + lastName: 'A l 1' + }] + ); + + firstName.setValue('A f 1', { modified: true }); + setTimeout(() => { + assert.equal(select.getValue(), 'c'); + assert.deepEqual(dataGrid.getValue(), + [{ + firstName: 'A f 1', + lastName: 'A l 1' + }] + ); + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }); + + it('Should calculate value for several DataGrid components', (done) => { + Formio.createForm(document.createElement('div'), twoWithAllowCalculatedOverride) + .then((form) => { + const select = form.getComponent('select'); + const dataGrid = form.getComponent('dataGrid'); + const dataGrid2 = form.getComponent('dataGrid2'); + + assert.deepEqual(dataGrid.getValue(), + [{ + firstName: '', + lastName: '' + }] + ); + assert.deepEqual(dataGrid2.getValue(), + [{ + firstName: '', + lastName: '' + }] + ); + + select.setValue('a', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), + [{ + firstName: 'A f 1', + lastName: 'A l 1' + }] + ); + assert.deepEqual(dataGrid2.getValue(), + [{ + firstName: 'A f 1', + lastName: 'A l 1' + }] + ); + + select.setValue('b', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), + [{ + firstName: 'B f 1', + lastName: 'B l 1' + }, + { + firstName: 'B f 2', + lastName: 'B l 2' + }] + ); + assert.deepEqual(dataGrid2.getValue(), + [{ + firstName: 'B f 1', + lastName: 'B l 1' + }, + { + firstName: 'B f 2', + lastName: 'B l 2' + }] + ); + + const firstName = form.getComponent(['dataGrid', 0, 'firstName']); + firstName.setValue('first name', { modified: true }); + const firstName2 = form.getComponent(['dataGrid2', 0, 'firstName']); + firstName2.setValue('first name 2', { modified: true }); + select.setValue('c', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), + [{ + firstName: 'first name', + lastName: 'B l 1' + }, + { + firstName: 'B f 2', + lastName: 'B l 2' + }] + ); + assert.deepEqual(dataGrid2.getValue(), + [{ + firstName: 'first name 2', + lastName: 'B l 1' + }, + { + firstName: 'B f 2', + lastName: 'B l 2' + }] + ); + done(); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }); }); diff --git a/src/components/datagrid/fixtures/comp-with-allow-calculate-override.js b/src/components/datagrid/fixtures/comp-with-allow-calculate-override.js index 55a3e367ff..da7c89f104 100644 --- a/src/components/datagrid/fixtures/comp-with-allow-calculate-override.js +++ b/src/components/datagrid/fixtures/comp-with-allow-calculate-override.js @@ -40,7 +40,7 @@ export default { lastName: '' } ], - calculateValue: "var temp = [\n {'firstName': 'initial 1','lastName': 'initial 2'},\n {'firstName': 'initial 1b','lastName': 'initial 2b'},\n ];\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", + calculateValue: "var temp = instance.defaultValue;\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", allowCalculateOverride: true, key: 'dataGrid', type: 'datagrid', diff --git a/src/components/datagrid/fixtures/index.js b/src/components/datagrid/fixtures/index.js index 417e0c136e..7b3ed8a9f5 100644 --- a/src/components/datagrid/fixtures/index.js +++ b/src/components/datagrid/fixtures/index.js @@ -13,4 +13,5 @@ import withConditionalFieldsAndValidations from './comp-with-conditional-compone import withLogic from './comp-with-logic'; import withCollapsibleRowGroups from './comp-with-collapsible-groups'; import withAllowCalculateOverride from './comp-with-allow-calculate-override'; -export { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, withCollapsibleRowGroups, withConditionalFieldsAndValidations, withDefValue, withLogic, withRowGroupsAndDefValue, modalWithRequiredFields, withAllowCalculateOverride }; +import twoWithAllowCalculatedOverride from './two-comp-with-allow-calculate-override'; +export { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, withCollapsibleRowGroups, withConditionalFieldsAndValidations, withDefValue, withLogic, withRowGroupsAndDefValue, modalWithRequiredFields, withAllowCalculateOverride, twoWithAllowCalculatedOverride }; diff --git a/src/components/datagrid/fixtures/two-comp-with-allow-calculate-override.js b/src/components/datagrid/fixtures/two-comp-with-allow-calculate-override.js new file mode 100644 index 0000000000..206982202e --- /dev/null +++ b/src/components/datagrid/fixtures/two-comp-with-allow-calculate-override.js @@ -0,0 +1,102 @@ +export default { + type: 'form', + display: 'form', + components: [ + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { + label: 'a', + value: 'a' + }, + { + label: 'b', + value: 'b' + }, + { + label: 'c', + value: 'c' + } + ] + }, + key: 'select', + type: 'select', + input: true + }, + { + label: 'Data Grid', + reorder: false, + addAnotherPosition: 'bottom', + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, + tableView: false, + defaultValue: [ + { + firstName: '', + lastName: '' + } + ], + calculateValue: "var temp = instance.defaultValue;\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", + allowCalculateOverride: true, + key: 'dataGrid', + type: 'datagrid', + input: true, + components: [ + { + label: 'First Name', + tableView: true, + key: 'firstName', + type: 'textfield', + input: true + }, + { + label: 'Last Name', + tableView: true, + key: 'lastName', + type: 'textfield', + input: true + } + ] + }, + { + label: 'Data Grid 2', + reorder: false, + addAnotherPosition: 'bottom', + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, + tableView: false, + defaultValue: [ + { + firstName: '', + lastName: '' + } + ], + calculateValue: "var temp = instance.defaultValue;\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", + allowCalculateOverride: true, + key: 'dataGrid2', + type: 'datagrid', + input: true, + components: [ + { + label: 'First Name', + tableView: true, + key: 'firstName', + type: 'textfield', + input: true + }, + { + label: 'Last Name', + tableView: true, + key: 'lastName', + type: 'textfield', + input: true + } + ] + } + ] +};