diff --git a/packages/devextreme/js/__internal/core/utils/m_object.ts b/packages/devextreme/js/__internal/core/utils/m_object.ts index d2d3aa009244..7380ae302169 100644 --- a/packages/devextreme/js/__internal/core/utils/m_object.ts +++ b/packages/devextreme/js/__internal/core/utils/m_object.ts @@ -41,7 +41,13 @@ const orderEach = function (map, func) { const getDeepCopyTarget = (item) => { if (isObject(item)) { - return Array.isArray(item) ? [] : {}; + if (Array.isArray(item)) { + return []; + } + if (!isPlainObject(item)) { + return Object.create(Object.getPrototypeOf(item)); + } + return {}; } return item; }; diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js index 8ea1458ca2da..83ce497f19ef 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.object.tests.js @@ -212,3 +212,12 @@ QUnit.test('deepExtendArraySafe copies array into object property deeply', funct timeArray[0] = 5; assert.deepEqual(objWithValue.time, [1, complexTime, 3]); }); + +QUnit.test('deepExtendArraySafe preserves custom object instances in arrays', function(assert) { + const CustomClass = function(value) { this.value = value; }; + const target = { items: [new CustomClass(1), new CustomClass(2)] }; + const changes = { items: [new CustomClass(3), new CustomClass(4)] }; + const result = objectUtils.deepExtendArraySafe(target, changes, true, false, false, objectUtils.newAssign); + assert.ok(result.items[0] instanceof CustomClass, 'First item preserves custom type'); + assert.ok(result.items[1] instanceof CustomClass, 'Second item preserves custom type'); +}); diff --git a/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js b/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js index fa57b16bac3f..0c46ea992c2f 100644 --- a/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.data/storeArray.tests.js @@ -1,5 +1,6 @@ import $ from 'jquery'; import ArrayStore from 'common/data/array_store'; +import Guid from 'core/guid'; import config from 'core/config'; import ErrorHandlingHelper from '../../helpers/data.errorHandlingHelper.js'; @@ -1115,6 +1116,45 @@ QUnit.test('prevent passing data as non-array', function(assert) { }); }); +QUnit.module('Push changes'); + +QUnit.test('push update preserves custom object instances inside arrays', function(assert) { + function createPerson(id, name) { + return { + id, + name, + guidArray: [new Guid(), new Guid(), new Guid()] + }; + } + + const guidPerson1 = new Guid(); + const guidPerson2 = new Guid(); + const person1 = createPerson(guidPerson1, 'Paul'); + const person2 = createPerson(guidPerson2, 'George'); + + const store = new ArrayStore({ + key: 'id', + data: [person1, person2] + }); + + const originalGuidArray = store.createQuery().toArray()[0].guidArray; + assert.ok(originalGuidArray[0] instanceof Guid, 'Guid instance is preserved before push'); + + person1.name = 'Will'; + store.push([{ type: 'update', key: guidPerson1, data: person1 }]); + + const updatedPerson = store.createQuery().toArray().find(item => item.id === guidPerson1); + assert.ok(updatedPerson, 'updated person is found'); + const updatedGuidArray = updatedPerson.guidArray; + + assert.strictEqual(updatedGuidArray.length, originalGuidArray.length, 'array length remains the same'); + updatedGuidArray.forEach((guidInstance, index) => { + assert.ok(guidInstance instanceof Guid, `item ${index} keeps Guid type after push`); + assert.strictEqual(guidInstance.toString(), originalGuidArray[index].toString(), 'Guid value is preserved'); + }); + assert.equal(updatedPerson.name, 'Will', 'person data is updated'); +}); + QUnit.module('Error handling'); QUnit.test('error in during query evaluation', function(assert) {