ng2-smart-table
ng2-smart-table copied to clipboard
Error when editing a same row twice
I am using ServerDataSource:
let data = event.newData;
this.tableService.update(data)
.then((result) => {
event.confirm.resolve(result[0]);
}).catch((err) => {
event.confirm.reject(err);
});
Error: Uncaught (in promise): Error: Element was not found in the dataset
Error: Element was not found in the dataset
at ServerDataSource.LocalDataSource.find (http://localhost:8080/vendor.bundle.js:842:31) [angular]
at LocalDataSource.update (http://localhost:8080/vendor.bundle.js:831:19) [angular]
at new ZoneAwarePromise (http://localhost:8080/polyfills.dll.js:4471:29) [angular]
at ServerDataSource.LocalDataSource.update (http://localhost:8080/vendor.bundle.js:830:16) [angular]
at http://localhost:8080/vendor.bundle.js:1578:26 [angular]
at Object.onInvoke (http://localhost:8080/vendor.dll.js:30260:37) [angular]
at Zone.run (http://localhost:8080/polyfills.dll.js:4033:43) [angular => angular]
at http://localhost:8080/polyfills.dll.js:4455:57 [angular]
at Object.onInvokeTask (http://localhost:8080/vendor.dll.js:30251:37) [angular]
at ZoneDelegate.invokeTask (http://localhost:8080/polyfills.dll.js:4194:40) [angular]
at Zone.runTask (http://localhost:8080/polyfills.dll.js:4071:47) [
at getStacktraceWithUncaughtError (http://localhost:8080/polyfills.dll.js:3790:12) [angular]
at new LongStackTrace (http://localhost:8080/polyfills.dll.js:3784:22) [angular]
at Object.onScheduleTask (http://localhost:8080/polyfills.dll.js:3840:18) [angular]
Did you solve this problem? Do you know the reason?
I am seeing the same problem after I switched from a local data source to a server data source. The server data source is using paging and extends LocalDataSource.
The steps are:
- Click edit on a row
- Click update - the row is updated on the server
- Click edit on the same row again
- Click update - the row is updated on the server but I get the error shown above when calling
event.confirm.resolve
Here is the onSaveConfirm method that gets called when clicking Update.
public onSaveConfirm(event: any): void {
const item = event.newData;
this.apiService.saveItem(item).subscribe(
savedItem => {
event.confirm.resolve(savedItem);
}
);
}
I thought maybe the problem is that I am passed a different (updated) object - savedItem - to resolve but I tried passing the original object - item - and the same problem happens.
My theory is that it has something to do with the page getting reloaded after the first edit and there is some mismatched data when the second edit happens. I see this order of events:
- First save occurs and event.confirm.resolve is called
- getElements on server data source is called to reload page 1 and it completes
- Edit is clicked and table moves to edit mode
- Update is clicked, save to server happens, event.confirm.resolve is called which causes exception.
Could item 2 (getElements) be causing some part of the model to be out of sync so during step 4 the "find" call cannot match the same element because they are different elements with the same data.
If this is the case a workaround may be to completely replace LocalDataSource instead of extending it. I will try that when I have time.
The problem is that there is code that assumes the object instance will be the same before and after the update occurs.
In local.data-source.ts the find method compares the instances using ===.
find(element: any): Promise<any> {
const found = this.data.find(el => el === element);
if (found) {
return Promise.resolve(found);
}
return Promise.reject(new Error('Element was not found in the dataset'));
}
In grid.ts the call to findRowByData compares using ===
this.source.onUpdated().subscribe((data) => {
const changedRow = this.dataSet.findRowByData(data);
changedRow.setData(data); //fails in here because data is undefined
});
data-set.ts:
findRowByData(data: any): Row {
return this.rows.find((row: Row) => row.getData() === data);
}
When using a server data source the elements change each time they are loaded. The first edit/update works because the object hasn't change but then getElements is called on the data source and the elements are different (in some places). When the edit/update occurs there is a mismatch.
To get around this I added update and find methods to completely override LocalDataSource to match elements by the id field I have in my objects. When the object is updated I copy the fields from the new values object passed to the update method and send that object to the updated event.
My first attempt was to not extend LocalDataSource but I still had the problem of the issue with data-set.ts comparing reference instead of testing by an id field.
Here is what I added to my data source class - which extends from LocalDataSource. I can now edit/update multiple times.
public update(element: Trigger, values: Trigger): Promise<any> {
return new Promise((resolve, reject) => {
this.find(element).then(found => {
//Copy the new values into element so we use the same instance
//in the update call.
element.name = values.name;
element.enabled = values.enabled;
element.condition = values.condition;
//Don't call super because that will cause problems - instead copy what DataSource.ts does.
///super.update(found, values).then(resolve).catch(reject);
this.emitOnUpdated(element);
this.emitOnChanged('update');
resolve();
}).catch(reject);
});
}
public find(element: Trigger): Promise<Trigger> {
//Match by the trigger id
const found: Trigger = this.data.find(el => el.id === element.id);
if (found) {
return Promise.resolve(found);
}
return Promise.reject(new Error('Element was not found in the dataset'));
}
Works for me! Thx!
@daddyman Thank you , Your solution works for me, But this bug should be solved without this work around .
I resolve that by changing :
-
LocalDataSource.prototype.find in local-data-dource.js var found = this.data.find(function (el) { return el.id === element.id; });
-
DataSet.prototype.findRowByData in data-set.js return this.rows.find(function (row) { return row.getData().id === data.id; });
I solved by invoking the refresh() method of the datasource after event.confirm.resolve() in onEditConfirm function: e.g .: onEditConfirm(ev){ this.httpapi.edit(ev.newData).subscribe( result => { if (result.status) { ... ev.confirm.resolve(result.data); this.source.refresh(); } else ...
this works fine for me.
thanks it is working for me
If anyone makes his own fork. Using isEqual from lodash does solve the problem with less changes, too.
// start of file
import { isEqual } from 'lodash';
...
// in local.data-source.ts
remove(element: any): Promise<any> {
this.data = this.data.filter(el => !isEqual(el, element));
return super.remove(element);
}
find(element: any): Promise<any> {
const found = this.data.find(el => isEqual(el, element));
if (found) {
return Promise.resolve(found);
}
return Promise.reject(new Error('Element was not found in the dataset'));
}
// in data-set.ts
findRowByData(data: any): Row {
return this.rows.find((row: Row) => isEqual(row.getData(), data));
}