jqGrid and ASP.NET MVC – Batch updates

Recently I've been asked to prepare a batch update scenario for jqGrid. After playing around with few prototypes I decided use inline editing for that purpose.
First we need to put all displayed rows into editable state. The best way to do this is to call editRow for each row during gridComplete event:
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.jqGrid.locale-en-3.8.2.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.jqGrid-3.8.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.json-2.2.min.js")" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#grid_id').jqGrid({
//Here you set up colModel, colNames and any other options
...
//This event fires after all the data is loaded into the grid
gridComplete: function() {
//Get ids for all current rows
var dataIds = $('#grid_id').jqGrid('getDataIDs');
for (var i = 0;i < dataIds.length; i++) {
//Put row in edit state
$("#grid_id").jqGrid('editRow', dataIds[i], false);
}
}
});
});
</script>
This way we can be sure, that every row loaded by jqGrid will end up in edit state. Now we need to add a button which will trigger the batch update. In the click event of that button we want to save every row to local data array. We can achieve that by using saveRow method, we just need to put 'clientArray' in third parameter. One important thing to remember here is that saveRow may throw an exception in case of validation errors - in such a case we will restore the original row and skip sending it to the server. Actual sending will be done by jQuery.ajax() method:
$('#button_id').click(function() {
var batch = new Array();
//Get ids for all current rows
var dataIds = $('#grid_id').jqGrid('getDataIDs');
for (var i = 0; i < dataIds.length; i++) {
try {
//Save row only to the grid
$('#grid_id').jqGrid('saveRow', dataIds[i], false, 'clientArray');
//Get row data
var data = $('#grid_id').jqGrid('getRowData', dataIds[i]);
//Data doesn't contains actual id
data.Id = dataIds[i];
//Add data to the batch
batch.push(data);
}
catch (ex) {
//If you are using editRules it might end up with exception
$('#grid_id').jqGrid('restoreRow', dataIds[i]);
}
}
//Send batch to the server
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
url: '@Url.Action("BatchUpdate", "Home")',
dataType: 'json',
data: $.toJSON(batch),
success: function(result) {
...
$('#grid_id').trigger('reloadGrid');
}
});
});
Now we have to receive the data on server side. Thanks to built in JSON model binding in ASP.NET MVC 3 we can do this with very simple action method:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult BatchUpdate(List<ViewModel> viewModelsBatch)
{
//Update your data storage here
...
}
Feel free to use this solution if you need something like this.

10 comments:

gsogol said...

Thanks a lot for the post. A couple of follow-up questions.

1. In my case I'm presenting thousands of records. I feel like, doing client-side looping may bog down the client's machine. Any suggestions here?

2. How would you remove the updated rows from the grid?

Tomasz Pęczek said...

At some point, the "looping" has to be done (at least to built a result, posting it row by row wouldn't be to efficient). In case of removing the rows, the sample above is using .trigger('reloadGrid') which will cause jqGrid to request new data from server (so you can skip those rows while sending), alternativly you can use .jqGrid('delRowData', rowId) to delete them one by one on client side.

gsogol said...

Will try that out, thanks again.

nickstips said...

Will this be added to your example download project? Does the allow you to add rows in batches as well or just to edit in batches?

Tomasz Pęczek said...

You can download it directly from "Source Code" on my project codeplex page (trunk/MVC/jqGrid Helper Examples/jqGrid.JeffSogolov). At this point is supports only updates but it should be possible to extend it for inserts.

Muhammad Farhan-ul-Haq Siddiqui said...

I'm gettign error in the method $.toJSON(batch) that object does not support this property or method. How can i pass batch array using $.toJASON(batch) method?

$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
url: '@Url.Action("BatchUpdate", "Home")',
dataType: 'json',
//data: '{viewModelsBatch: '+batch+'}',
data: $.toJSON(batch),
success: function(result) {
//...
$('#jqgProducts').trigger('reloadGrid');
}
});

Tomasz Pęczek said...

Are you sure you have referenced jquery.json-2.2.min.js?

Stephen said...

I find that if I put 100+ records into edit mode, where I have a checkbox and drop-down list that are editable, performance is absolutely horrible.

Is anyone else seeing this?

Anonymous said...

Without jqGrid being able to pass the contents of the grid over to the controller, this seems to be what I'm looking for. My situation is this, I'm pulling a list of values from a csv file then displaying them in a jqGrid. I would like to confirm that the data in the grid is what needs to be inserted into a database.
Based on that, your code seems to work, but I'm having issues. I can see you have posted an example on your codeplex page, but I can't seem to find it. Can you point me in the right direction?

Tomasz Pęczek said...

This sample isn't available as zip download, you need to download source code directly from repository. Here is SVN link -> https://tpeczek.svn.codeplex.com/svn/trunk/MVC/jqGrid Helper Examples/jqGrid.JeffSogolov (you can also download entire repository directly from Codeplex -> Source Code)