automerge-classic
automerge-classic copied to clipboard
Cannot move objects in arrays since 0.14.1
The error Cannot assign an object that already belongs to an Automerge document
seems to trigger when trying to move an item in an array since the last patch. Here is an example to reproduce:
const automerge = require('automerge');
const s1 = automerge.init();
const s2 = automerge.change(s1, 'create array', doc => {
doc.myArray = [{foo: '1'}, {bar: '2'}];
});
const s3 = automerge.change(s2, 'move item in array', doc => {
const removedItem = doc.myArray.splice(0, 1);
doc.myArray.insertAt(1, removedItem);
});
If this is an expected error, what is the recommended way of moving things in arrays?
Thanks @viktorkalajo for the bug report. This should be possible, sorry to hear that it's not working at the moment. I will look into it, but in the meantime, as a hacky workaround you can deep-clone the removed object by serialising and deserialising to JSON:
const s3 = automerge.change(s2, 'move item in array', doc => {
const removedItems = doc.myArray.splice(0, 1);
doc.myArray.insertAt(1, ...JSON.parse(JSON.stringify(removedItems)));
})
The workaround has an issue
Assume
-
Document
{ sourceItemsArray: [{ objectId: 1, value: { a: 1, b: 2 } }], destinationItemsArray: [{ objectId: 2, value: { a: 3, b: 4 } }] }
-
and two concurrent actors are editing the document
Event sequence
- The first one changes at T1 the value of the item in sourceItemsArray, to {a: 101}
- The second one then moves at T2 the same item from sourceItemsArray to the destination
- On merge, the result document is
{ sourceItemsArray: [], destinationItemsArray: [{ objectId: 2, value: { a: 3, b: 4 } },{ objectId: 3, value: { a: 1, b: 2 } }] }
- Expected document is
{ sourceItemsArray: [], destinationItemsArray: [{ objectId: 2, value: { a: 3, b: 4 } },{ objectId: 1, value: { a: 101, b: 2 } }] }
So the change at T1 is lost. Note the differences are:
- objectId (from 1 to 3)
- value.a == 1 but it should be value.a == 101
Yes, that is unfortunately an expected behaviour of the workaround, since it removes the objects from the old location and creates fresh objects (with the old contents) in the new location.
A proper move operation is planned, but unfortunately it's going to be a bit of work.
Not sure if it helps, but it's also impossible to assign objects removed from arrays or other objects to other object properties as well. Any thought on how to work around this without de-soulifying the object?
const automerge = require('automerge')
const s1 = automerge.init()
const s2 = automerge.change(s1, 'create array', doc => {
doc.myArray = [{ foo: '1' }, { bar: '2' }]
})
const s3 = automerge.change(s2, 'move item out of array', doc => {
const removedItem = doc.myArray.splice(0, 1)
doc.someProp = removedItem
})
and
...
const s2 = automerge.change(s1, 'create object', doc => {
doc.myProp = { a: { foo: '1' }, b: { bar: '2' } }
})
const s3 = automerge.change(s2, 'move item out of object', doc => {
const removedItem = doc.myProp.a
delete doc.myProp.a
doc.removedItem = removedItem
})
both result in:
TypeError: Cannot assign an object that already belongs to an Automerge document. See https://github.com/automerge/automerge#making-fine-grained-changes
@beorn Sorry, the deep cloning workaround is the only solution for now, until the proper move operation lands.
Closing this issue as there is nothing to be done for now. Support for a proper move operation is planned, which will address this issue.
@ept is this a duplicate of another issue? if not, might it be possible to re-open until the move lands as a means to notify folks who are interested to know?
Okay @hcientist, makes sense. Reopening the issue to serve as a kind of documentation.