Introduction
There are a lot of articles out there that show you how to integrate with the jQuery jqGrid Plugin from a listing, paging, sorting approach, but I haven’t seen many that show the integration with the add/edit/delete features that jqGrid offers.
It turns out that it isn’t very difficult to do, but it took quite a bit of digging in the jqGrid API documentation for me to find all the things I needed.
The following article will show how to customize the add/edit/delete modal experience inside of the jqGrid with ASP.NET MVC.
Contact ViewModel
First, you start off with your ViewModel. In this case the following is really bare bones. I haven’t annotated the properties with metadata because I actually do that manually in the jqGrid columns. That isn’t optimal, but it would be nice to have these automatically mapped. That sounds like another blog post ;)
01.
public
class
ContactViewModel
02.
{
03.
public
System.Guid ContactId {
get
;
set
; }
04.
05.
public
string
Name {
get
;
set
; }
06.
07.
public
string
Email {
get
;
set
; }
08.
09.
public
string
PhoneNumber {
get
;
set
; }
10.
11.
public
DateTime DateOfBirth {
get
;
set
; }
12.
13.
public
bool
IsMarried {
get
;
set
; }
14.
}
Contact ViewThe following code setups up the jqGrid to support add, edit, and delete.
The first function you’ll see is a custom validator that checks to see if the phone number has a length of 14. Yes, it isn’t bullet-proof validation by any stretch of the imagination, but its more of an example of what can be done.
Next you’ll see the updateDialog object literal defining the look and behavior of the add, edit, and delete dialog windows. The main property to define is the URL where the AJAX requests will post the data. You can also control whether the dialog closes immediately, if it’s a modal dialog, etc…
The next important thing to notice is the “key: true” property of the ContactId column. If you don’t set this property then the POST for the delete command only send the relative ID that jqGrid generates, not the ContactId that you need. So, this is important ;)
Note: You’ll see some code below setting global properties for the jqGrid such as the title of the dialogs, buttons, etc. If you don’t do this then you’ll get generic titles. I figured these customizations made the user experience a little nicer, but things will work just find without them.
01.
function
isValidPhone(value, name) {
02.
console.log(
'isValidPhone'
);
03.
var
errorMessage = name +
': Invalid Format'
;
04.
var
success = value.length === 14;
05.
return
[success, success ?
''
: errorMessage];
06.
}
07.
08.
$(document).ready(
function
() {
09.
var
updateDialog = {
10.
url:
'<%= Url.Action("Update", "Contact") %>'
11.
, closeAfterAdd:
true
12.
, closeAfterEdit:
true
13.
, afterShowForm:
function
(formId) {
14.
$(
"#PhoneNumber"
).mask(
"(999) 999-9999"
);
15.
$(
"#DateOfBirth"
).datepicker();
16.
}
17.
, afterclickPgButtons:
function
(whichbutton, formid, rowid) {
18.
$(
"#PhoneNumber"
).mask(
"(999) 999-9999"
);
19.
}
20.
, modal:
true
21.
, width:
"400"
22.
};
23.
24.
$.jgrid.nav.addtext =
"Add"
;
25.
$.jgrid.nav.edittext =
"Edit"
;
26.
$.jgrid.nav.deltext =
"Delete"
;
27.
$.jgrid.edit.addCaption =
"Add Contact"
;
28.
$.jgrid.edit.editCaption =
"Edit Contact"
;
29.
$.jgrid.del.caption =
"Delete Contact"
;
30.
$.jgrid.del.msg =
"Delete selected Contact?"
;
31.
32.
$(
"#list"
).jqGrid({
33.
url:
'<%= Url.Action("List", "Contact") %>'
,
34.
datatype:
'json'
,
35.
mtype:
'GET'
,
36.
colNames: [
'ContactId'
,
'Name'
,
'Date of Birth'
,
'E-mail'
,
'Phone Number'
,
'Married'
],
37.
colModel: [
38.
{ name:
'ContactId'
, index:
'ContactId'
, width: 40, align:
'left'
, key:
true
, editable:
true
, editrules: { edithidden:
false
}, hidedlg:
true
, hidden:
true
},
39.
{ name:
'Name'
, index:
'Name'
, width: 300, align:
'left'
, editable:
true
, edittype:
'text'
, editrules: { required:
true
}, formoptions: { elmsuffix:
' *'
} },
40.
{ name:
'DateOfBirth'
, index:
'DateOfBirth'
, width: 200, align:
'left'
, formatter:
'date'
, datefmt:
'm/d/Y'
, editable:
true
, edittype:
'text'
, editrules: { required:
true
, date:
true
}, formoptions: { elmsuffix:
' *'
} },
41.
{ name:
'Email'
, index:
'Email'
, width: 200, align:
'left'
, formatter:
'mail'
, editable:
true
, edittype:
'text'
, editrules: { required:
true
, email:
true
}, formoptions: { elmsuffix:
' *'
} },
42.
{ name:
'PhoneNumber'
, index:
'PhoneNumber'
, width: 200, align:
'left'
, editable:
true
, edittype:
'text'
, editrules: { required:
true
, custom:
true
, custom_func: isValidPhone }, formoptions: { elmsuffix:
' *'
} },
43.
{ name:
'IsMarried'
, index:
'IsMarried'
, width: 200, align:
'left'
, editable:
true
, edittype:
'checkbox'
, editoptions: { value:
"True:False"
}, editrules: { required:
true
}}],
44.
pager: $(
'#listPager'
),
45.
rowNum: 1000,
46.
rowList: [1000],
47.
sortname:
'ContactId'
,
48.
sortorder:
"desc"
,
49.
viewrecords:
true
,
50.
imgpath:
'/Content/Themes/Redmond/Images'
,
51.
caption:
'Contact List'
,
52.
autowidth:
true
,
53.
ondblClickRow:
function
(rowid, iRow, iCol, e) {
54.
$(
"#list"
).editGridRow(rowid, prmGridDialog);
55.
}
56.
}).navGrid(
'#listPager'
,
57.
{
58.
edit:
true
, add:
true
, del:
true
, search:
false
, refresh:
true
59.
},
60.
updateDialog,
61.
updateDialog,
62.
updateDialog
63.
);
64.
});
Contact Controller Update Action
The add/update/delete feature takes one URL where you can change the logic based on the operation type. The MVC Modal Binder will map the fields into your ViewModel in most cases. The exception is the “id” that is passed on the delete operation, but there is a way to get around that later in this post ;)
01.
public
ActionResult Update(ContactViewModel viewModel, FormCollection formCollection)
02.
{
03.
var operation = formCollection[
"oper"
];
04.
if
(operation.Equals(
"add"
) || operation.Equals(
"edit"
))
05.
{
06.
repository.SaveOrUpdate(
new
ContactViewModel
07.
{
08.
ContactId = viewModel.ContactId,
09.
DateOfBirth = viewModel.DateOfBirth,
10.
Email = viewModel.Email,
11.
IsMarried = viewModel.IsMarried,
12.
Name = viewModel.Name,
13.
PhoneNumber = viewModel.PhoneNumber
14.
});
15.
}
16.
else
if
(operation.Equals(
"del"
))
17.
{
18.
repository.Delete(
new
ContactViewModel
19.
{
20.
ContactId =
new
Guid(formCollection[
"id"
])
21.
});
22.
}
23.
24.
return
Content(repository.HasErrors.ToString().ToLower());
25.
}
What About Using Complex Keys?
Instead of having ContactId (“key: true”) as your key to delete, you might have a more complex key to identify which item to delete. As it turns out, you can bind to the onclickSubmit event of the add/edit/delete dialog and change what data is POST’ed to the controller.
A nice side effect of this is that you name your property such that the MVC Modal Binder works.
Updated Dialog Object Literal
01.
var
updateDialog = {
02.
url:
'<%= Url.Action("Update", "Contact") %>'
03.
, closeAfterAdd:
true
04.
, closeAfterEdit:
true
05.
, afterShowForm:
function
(formId) {
06.
$(
"#PhoneNumber"
).mask(
"(999) 999-9999"
);
07.
$(
"#DateOfBirth"
).datepicker();
08.
}
09.
, afterclickPgButtons:
function
(whichbutton, formid, rowid) {
10.
$(
"#PhoneNumber"
).mask(
"(999) 999-9999"
);
11.
}
12.
, modal:
true
13.
, onclickSubmit:
function
(params) {
14.
var
ajaxData = {};
15.
16.
var
list = $(
"#list"
);
17.
var
selectedRow = list.getGridParam(
"selrow"
);
18.
rowData = list.getRowData(selectedRow);
19.
ajaxData = { ContactId: rowData.ContactId };
20.
21.
return
ajaxData;
22.
}
23.
, width:
"400"
24.
};
Updated Contact Controller Update Action
01.
public
ActionResult Update(ContactViewModel viewModel, FormCollection formCollection)
02.
{
03.
var operation = formCollection[
"oper"
];
04.
if
(operation.Equals(
"add"
) || operation.Equals(
"edit"
))
05.
{
06.
repository.SaveOrUpdate(
new
ContactViewModel
07.
{
08.
ContactId = viewModel.ContactId,
09.
DateOfBirth = viewModel.DateOfBirth,
10.
Email = viewModel.Email,
11.
IsMarried = viewModel.IsMarried,
12.
Name = viewModel.Name,
13.
PhoneNumber = viewModel.PhoneNumber
14.
});
15.
}
16.
else
if
(operation.Equals(
"del"
))
17.
{
18.
repository.Delete(
new
ContactViewModel
19.
{
20.
ContactId = viewModel.ContactId
21.
});
22.
}
23.
24.
return
Content(repository.HasErrors.ToString().ToLower());
25.
}
Conclusion
I hope you found the above article of some use in your everyday coding. The jqGrid also provides an inline editing feature similar to what you might experience in an Excel grid. You might look into that if you are interested.
Please give me your feedback. Thanks!