I've successfully used the igGrid to hook into our Web API. Create, Remove and Delete operations are firing nicely and playing nicely together. The only scenario that is currently failing is this:
In the "iggridupdatingrowadded" event I call "SaveChanges" to the grid to force the API call.
rowAdded: function (e, ui) { var original = ui; ui.owner.grid.saveChanges(function (record, status, response) { var row = this.findRecordByKey(original.rowID); row[original.owner.grid.options.primaryKey] = record[original.owner.grid.options.primaryKey]; this.updateRow(original.rowID, row); }); }
When the transaction commits, it is failing to map the transaction with an -1 ID when it should be 100.
Technical Details:
v14.2
Method: _commitTransaction
this.origDs has a record value of -1.
Unhandled exception at line 244, column 17855 in http://localhost:51580/admin/Scripts/IG/js/infragistics.core.js
0x800a138f - JavaScript runtime error: Unable to set property 'Id' of undefined or null reference
How can I correctly programmatically change the primary key value so the entire grid will function as normal?
Thanks,
Hello,
Thank you for posting in our community.
What I can suggest in order to have a matching ids on the client and server is to bind igGrid to the new(updated) data on every success callback of the saveChanges function. This could be achieved by updating the igGrid data source instance with the values from the underlying data base. The new data could be sent in the response and afterwards used to data bind igGrid. For example:
//add the updated data with the new id`s matching actual records in the response and send it back to the success callback $(".selector").igGrid("saveChanges", function (data) { //Set $(".selector").igGrid("option", "dataSource", data); $(".selector").igGrid("dataBind"); }
//add the updated data with the new id`s matching actual records in the response and send it back to the success callback
$(
".selector"
).igGrid(
"saveChanges"
,
function
(data) {
//Set
"option"
"dataSource"
, data);
}
I hope you find this suggestion helpful.
Please let me know if you have any additional questions regarding this matter.
The data from the saveChanges event is that of a single record. Not the entire datasource. So if I use the code you provided then the grid will go from 100 records, to one record.
Would just calling dataBind cause the grid to reach out to the original url from before and just grab all of the current records?
Hello Karthik,
Thank you for getting back to me.
By design igGrid is a client side control. This means that it has no information for the changes in the data base on the server side. If your requirement is to sync them you will have to ensure that the data source available on the client side is the same as the one on the server side, which could be achieved using my suggestion to send the updated data and dataBind grid in the success callback. This data, that is sent in the response should consist of all the rows of the table in the underlying data source not just the recent;y updated one.
In case that your are using a remote data source when dataBind is called a get request to the dataSourceUrl is going to be triggered which will get the new data source unless it is cached.
By design new records in igGrid display at the bottom of the grid and currently we do not have an option to insert row on any specified position.
Your assumption is correct, when called the dataBind method will make a request to the dataSourceUrl and if your data is not cached, however retrieved directly from he data source it should return the updated records.
Additionally, unbound column could not be used as a primary key since the primary key should point to an actually existing field in the underlying data source.
Please let me know if you have any additional questions
I am glad that you managed to resolve your issue.
Thank you for sharing your feedback with us and I believe the other members of the community may benefit form your experience.
So the template is tricky. We made the assumption that the template works like the igTemplating. This is INCORRECT. It only allows for one specific value which is ${id} (pay attention to the casing). I have a primary key of Id so I suspected it followed the Primary Key syntax, which is also INCORRECT. If your primary key is called "MyPrimaryKey" then you still specify ${id} in your rest setting template. I guess this also means that the template doesn't support hierarchys or parent child relationships. For example:
DELETE: "/api/location/${locationId}/location/${id}"
Remove a carrier for a specific location. Does the igGrid support this type of syntax? The locationId would come from the row object that was selected.
Our problem is now solved, but I still wanted to make this point clear for others searching for an identical issue.
I've played around with the settings and it still doesn't want to work.
I see why updates are working. It is because our API is grabbing the ID off of the request body, not the URL. The URL for the updates are also wrong. They are being encoded when I expect the value of the ID to be seeded in the URL.
I expect:
[POST] /admin/api/carrier/1 [DELETE] /admin/api/carrier/1
I tried toggling the value encodeRemoveInRequestUri but it did nothing.
The method you aren't familiar with is $.ced.createUri. It formats URL's for us and appends the application root URL if needed. In this case, I tried removing it from the equation and it still encoded the URL.
Sorry I misunderstood what you meant by data binding. I modified the event and now it is correctly seeding the value. We have another event handler for deleting rows, here's the entire igGrid definition:
$.ced.ig.grid({ id: "igGridCarriers", allowPaging: false, allowHiding: false, allowUndoRedo: false, emptyDataText: "THERE ARE NO CARRIERS AVAILABLE. CLICK TO ADD A CARRIER.", primaryKey: "Id", columns: [ { key: "Id", dataType: "number", hidden: true }, { key: "CarrierDescription", dataType: "string", headerText: "Description" }, { key: "StandardCarrierAlphaCode", dataType: "string", headerText: "AlphaCode" }, { key: "Display", dataType: "string", headerText: "Display", unbound: true, template: "${StandardCarrierAlphaCode} - ${CarrierDescription}" }, { key: "SortOrder", dataType: "number", hidden: true }, { key: "CreatedBy", dataType: "number", hidden: true }, { key: "CreatedDate", dataType: "date", hidden: true }, { key: "UpdatedBy", dataType: "number", hidden: true }, { key: "UpdatedDate", dataType: "date", hidden: true }, { key: "Actions", dataType: "string", headerText: "", width: "148px", align: "center", template: "×", unbound: true } ], features: [ { name: "Filtering", columnSettings: [ { columnKey: "Display", allowFiltering: false }, { columnKey: "Actions", allowFiltering: false }, ] }, { name: "Sorting", columnSettings: [ { columnKey: "Display", allowSorting: false }, { columnKey: "Actions", allowSorting: false }, ] }, { name: "Updating", editMode: "cell", autoCommit: false, enableAddRow: true, enableDeleteRow: false, columnSettings: [ { columnKey: "Display", readOnly: true }, { columnKey: "Actions", readOnly: true } ], rowAdding: function (e, ui) { ui.values.CreatedDate = $.ig.formatter(ui.values.CreatedDate || new Date(), "date", "MM/dd/yyyy h:mm:ss tt", true, true, null); ui.values.CreatedBy = ui.values.CreatedBy || 1; ui.values.UpdatedDate = $.ig.formatter(new Date(), "date", "MM/dd/yyyy h:mm:ss tt", true, true, null); ui.values.UpdatedBy = 1; }, rowAdded: function (e, ui) { var original = ui; ui.owner.grid.saveChanges(function (record, status, response) { ui.owner.updateRow(original.rowID, record); original.owner.grid.dataBind(); }); }, editCellEnding: function (e, ui) { if (ui.rowAdding) { return; } if (ui.update === false) { return false; } var row = ui.owner.grid.findRecordByKey(ui.rowID); row.CreatedDate = $.ig.formatter(row.CreatedDate || new Date(), "date", "MM/dd/yyyy h:mm:ss tt", true, true, null); row.CreatedBy = row.CreatedBy || 1; row.UpdatedDate = $.ig.formatter(new Date(), "date", "MM/dd/yyyy h:mm:ss tt", true, true, null); row.UpdatedBy = 1; ui.owner.updateRow(ui.rowID, row); }, editCellEnded: function (e, ui) { if (ui.rowAdding) { return; } ui.owner.grid.dataSource.saveChanges(); } } ], restSettings: { create: { batch: false, template: $.ced.createUri("/api/carrier"), url: $.ced.createUri("/api/carrier") }, update: { batch: false, template: $.ced.createUri("/api/carrier/${Id}"), url: $.ced.createUri("/api/carrier") }, remove: { batch: false, template: $.ced.createUri("/api/carrier/${Id}"), url: $.ced.createUri("/api/carrier") } }, dataSource: $.ced.createUri("/api/carrier") });
Our remove template is setup for deleting items but this is what we see: DELETE /admin/api/carrier/$%7BId%7D HTTP/1.1 The template is escaping the values instead of seeding it with the ID. Updates work great! What gives?
After playing with the settings I see that setting an unbound column up as a primary key does not work for the method findRecordByKey. Nor does it set the data-id attribute on the TR. It is set to undefined.
This sucks. In theory it should work.
It doesn't make sense for an API service to return the entire data source for a POST request. We only return the record that was just created. Furthermore, the grid DOES NOT call the original URI used for the get request. I had to pass in the URL again to reset the data source.
When I read the documentation for igGridUpdating it states:
http://www.igniteui.com/help/iggrid-updating
Updating API
In addition to the updating UI, there is a rich API for adding, updating, and deleting rows programmatically.
When adding a row programmatically, a primary key value is not required. If you do not specify a primary key, a value is provided for you based off of the number of rows in the grid. This can later be processed on the server and replaced with a valid primary key as required by your persistence medium.
I processed the record on the server, now I want to replace the primary key with a valid one from my API. How? Data binding is okay - if and only if it is just for that one record. But we know that's not how it works. I need to only update that single record.