Introduction
If you are an ASP .NET developer, you are familiar with editing (Insert, Update and Delete) GridView
’s row data using Item Template and Edit Template. You will be surprised, when you find that WebGrid of MVC does not have Edit Template and does not allow user to edit row data of WebGrid.
Using JQuery, you can allow user to edit data in WebGrid. In this topic, I will walk you through:
- How to populate data in WebGrid
- Using JQuery, edit data in WebGrid
Please note that I am concentrating on the functionality instead of making it nice. In this demonstration, I am going to use Student
object for student
information.
1.0 Create MVC 4 Web Application
Using Visual Studio 2012, create a new project with MVC 4 Web Application. Select the template either Internet or Intranet application. I have selected the Internet Application Template. Make sure Razor is selected in the View Engine.
By default, Visual Studio creates several files for Controllers, Models and Views. In this tutorial, you are going to modify the landing page of the application. In other words, you are going to use the Index.cshtml view to display Student
data.
2.0 Add JQuery Scripts to Project
Open the _Layout.cshtml from the Views->Shared folder and add the following JQuery references just before the end tag (</head>
) of Head
.
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery-ui-1.8.24.min.js"></script>
3.0 Create Data Model (Student)
From the Solution Explorer, right click on the Models and select Add option from context menu. Then select Class option and name it Student
. Now, the Models folder should have the Student.cs class.
Open Student.cs and add fields as shown below. You may add validation and others attribute. To concentrate only on the editing, keep it simple.
namespace DrillDownDemo.Models
{
public class Student
{
public int ID { get; set; }
public String Name { get; set; }
public String email { get; set; }
}
}
4.0 Add Style Sheet for WebGrid
In this demonstration, you will use different background color for WebGrid’s alternate rows and footer. To accomplish this, you are going to use the CSS file. Right click on the Content folder and select New Item from the Add context menu. Select Style Sheet from the Add New Item dialog box. Name it Student
and click on Add button.
Open the Student.css file and add the following CSS statements in the Student.css file:
.oddRowStyle
{
background-color: #00CCFF;
color: #FFFFFF;
}
.evenRowStyle
{
background-color: #CCCCCC;
}
.tfootrow
{
background-color: #3366CC;
color: #FFFFFF;
}
.tfootrow a
{
color:#FFFFFF;
}
After saving the Student.css file, add the CSS link in the _Layout.cshtml so that all pages can use the style sheet. Add the following lines inside the HEAD
tag of _Layout.cshtml file.
<link href="~/Content/Student.css" rel="stylesheet" />
5.0 Modify Controller (HomeController)
For this demo application, as you are using the landing page, modify HomeController
. In real life application, you may need to display the data in different view. In that case, you will need to add/modify the corresponding controller.
For simplicity, use static
data, instead of using backend data storage. Add a static
list of Student
object in the HomeController
.
public static List<Models.Student> Students = new List<Models.Student>()
{
new Models.Student {
ID = 1,
Name = "Adam Worth",
email = "adam.worthh@abc.edu",
},
new Models.Student {
ID = 2,
Name = "John Doe",
email = "john.doe@abc.edu",
},
new Models.Student {
ID = 3,
Name = "Gorge Klene",
email = "gorge.klene@abc.edu",
}
};
To send the Students
list to the Index
view, modify the Index()
method and return the Students
in the view. The line of code is as follows:
public ActionResult Index()
{
return View(Students);
}
6.0 Modify the View (index.cshtml)
6.1 Display Data using WebGrid
Open Index.cshtml file from the Views->Home folder. You need to specify the Data Model for this view. At the first line of the Index.cshtml file, add the following:
@model IEnumerable<ManipulateWebGridDemo.Models.Student>
This updates the Model
property of the associated System.Web.Mvc.ViewDataDictionary
with the list of student
objects. The Model
property will be used in the WebGrid constructor as a parameter.
If you like, you may add the ViewBag.Title
as follows:
@{
ViewBag.Title = "Manipulate WebGrid Data";
}
Now, you need to add the code for WebGrid
. Define a <div>
with ID and refer the div
’s id
in the ajaxUpdateContainerId
attribute of WebGrid
control that will allow WebGrid
to update data asynchronously using Ajax. As there are only 3 records of Student
in the data and if you would like to see the behavior of the WebGrid in different page, define the rowsPerPage
with value 2.
<div id="ajaxgrid">
@{
var grid = new WebGrid(Model, ajaxUpdateContainerId: "ajaxgrid",
rowsPerPage: 2);
}
</div>
Next, you need to define the columns. In the first column, define different Actions (Edit, Save, Cancel and Delete). There are two different modes for view – one to display and another to edit. Hence, you can see Edit and Delete actions have “display” in the class attribute and Save and Cancel action has “edit” in the class attribute. Those classes will be used in JQuery to determine the active mode.
To capture user’s click event of action buttons, define another class attribute (e.g., XXXXXX-btn). Please, note that those classes do not need to be in the CSS file. Those are only used in the JQuery to find controls and capture the event.
IEnumerable<WebGridColumn> cols =
new List<WebGridColumn> {
new WebGridColumn{
Format =
@<text>
<a href='#' class="display edit-btn">Edit</a>
<a href="#" class="edit save-btn">Save</a>
<a href="#" class="edit cancel-btn">Cancel</a>
<a href="#" class="display delete-btn">Delete</a>
</text>
},
new WebGridColumn{
Header="ID",
Format = @<text>
<span class="display">
<label id="lblID">@item.ID</label>
</span>
<input type="text" id="inID" value="@item.ID"
class="edit" readonly="true"/>
</text>
},
new WebGridColumn{
Header="First Name",
Format = @<text>
<span class="display">
<label id="lblName">@item.Name</label>
</span>
<input type="text" id="inName"
value="@item.Name"
class="edit" />
</text>
},
new WebGridColumn{
Header="Email",
Format = @<text>
<span class="display">
<label id="lblEmail">@item.Email</label>
</span>
<input type="text" id="inEmail"
value="@item.Email"
class="edit" />
</text>
}
};
Each data column has two controls for display and edit mode. Label controls will be visible for display mode and Input controls will be visible for edit mode. Those controls need to be defined under the FORMAT
attributes of WebGridColumn
definition. Label
control does not have class attributes, hence it is wrapped with <span>
tag.
Next call the GetHtml
method of WebGrid, so that Razor Engine can generate corresponding HTML for it. After the end of <div>
tag, add the following statement:
@grid.GetHtml(columns:cols, rowStyle:"oddRowStyle", alternatingRowStyle: "evenRowStyle")
If you run the application, at this point, you can see that both Label
and Input control’s data are visible in the page. Here is a sample image of it.
In the next section, you are going to add JQuery fuctionalities to handle active mode of either display or edit.
Full Code Segment
@model IEnumerable<ManipulateWebGridDemo.Models.Student>
@{
ViewBag.Title = "Manipulate WebGrid Data";
}
<div id="ajaxgrid">
@{
var grid = new WebGrid(Model, ajaxUpdateContainerId: "ajaxgrid", rowsPerPage: 2);
IEnumerable<WebGridColumn> cols =
new List<WebGridColumn> {
new WebGridColumn{
Format = @<text>
<a href='#' class="display edit-btn">Edit</a>
<a href="#" class="edit save-btn">Save</a>
<a href="#" class="edit
cancel-btn">Cancel</a>
<a href="#" class="display
delete-btn">Delete</a>
</text>
},
new WebGridColumn{
Header="ID",
Format = @<text>
<span class="display">
<label id="lblID">@item.ID</label>
</span>
<input type="text" id="inID" value="@item.ID"
class="edit" readonly="true"/>
</text>
},
new WebGridColumn{
Header="First Name",
Format = @<text>
<span class="display">
<label id="lblName">@item.Name</label>
</span>
<input type="text" id="inName"
value="@item.Name"
class="edit" />
</text>
},
new WebGridColumn{
Header="Email",
Format = @<text>
<span class="display">
<label id="lblEmail">@item.Email</label>
</span>
<input type="text" id="inEmail"
value="@item.Email"
class="edit" />
</text>
}
};
}
</div>
@grid.GetHtml(columns:cols, rowStyle:"oddRowStyle", alternatingRowStyle: "evenRowStyle")
6.2 Adding JQuery Method
You are going to use JQuery to control the Display and Edit mode. After adding all of the HTML mentioned above, add a <script>
section as follows:
<script type="text/javascript">
$(function () {
});
</script>
There are three different actions for data manipulation – insert, update and delete. In the following sections, you will find JQuery and HomeController
code for all of the three actions.
6.2.1 Insert Record
WebGrid does not have method to access footer section during the defining of it. As a result, you need to add a footer section into the HTML code generated by Razor for WebGrid using JQuery. Footer section will be used to insert records. Section for insert, should be above of the page index.
Define a variable (tfootAdd
) with the row and all columns. First column contains all three action commands. Rest of the three columns define the fields of Student
object. Fields of Student
objects do not need to be visible at display mode. Hence, you can use <span>
tags without any information and have class attribute “display
”. <input>
tags are used to capture user input at edit mode.
var tfootAdd = "<tr class='tfootrow'>" +
"<td/>" +
"<td> " +
"<a href='#' class='display ins-btn' >Insert</a>" +
"<a href='#' class='edit inssave-btn'>Save</a>" +
"<a href='#' class='edit cancel-btn'>Cancel</a>" +
"</td>" +
"<td>" +
"<span class='display'/>" +
"<input type='text' id='inID' value='' class = 'edit'/> " +
"</td>" +
"<td>" +
"<span class='display'/>" +
"<input type='text' id='inName' value='' class = 'edit'/>" +
"</td>" +
"<td>" +
"<span class='display'/>" +
"<input type='text' id='inEmail' value='' class = 'edit'/>" +
"</td>" +
"</tr>"
Then, find the first row of footer section (tfoot
) and then add the variable (tfootAdd
).
$("table tfoot tr:first").before(tfootAdd);
When the application starts, all rows of WebGrid should be in display mode. After that, based on user selection, it should switch between display and edit mode for each row. To accomplish this, add the following JQuery which will find all controls with ‘edit
’ class attribute and hide them.
$('.edit').hide();
When user clicks on Insert and/or Edit button of a row, that particular row should change to edit mode. In the edit mode, if user clicks on Cancel button, then that row should go back to display mode. Capture the click
event of Insert, Edit and Cancel button by using the “ins-btn
”, “edit-btn
” and “cancel-btn
” attributes. In the click
event, find the parent row of Insert, Edit or Cancel. Then find the “edit” and “display” attribute of the row and call the toggle function of JQuery. Toggle function of JQuery will switch the display mode.
$('.ins-btn, .edit-btn, .cancel-btn').on("click", function () {
var tr = $(this).parents('tr:first');
tr.find('.edit, .display').toggle();
});
Capture the Click
event of save action for Insert using JQuery and then call the method of HomeController
to save the data. First, define the click method of the Save button (inssave-btn
). Inside this function, grab the data for ID, Name and Email. Using the data, you need to create a Student
object, which will be used to send data to the InsertData
method (described in a later section) of the HomeController
. InsertData
method of HomeController
will be called using Ajax function of JQuery. InsertData
method will return 1 for success and 0 for error. Those values are going to capture in the isSuccess
variable. When the process of insert is completed, Ajax function will call the done
event. If InsertData
method returns success
, then it will show success
message, go back to display mode and refresh the page. Otherwise, it will show the error
message and stay in edit mode.
$('.inssave-btn').on("click", function () {
var tr = $(this).parents('tr:first');
var name = tr.find("#inName").val();
var id = tr.find("#inID").val();
var email = tr.find("#inEmail").val();
var isSuccess = -1;
var Student =
{
"ID": id,
"Name": name,
"email": email
};
$.ajax({
url: '/Home/InsertData/',
data: JSON.stringify(Student),
type: 'POST',
contentType: 'application/json; charset=utf-8',
success: function (result) {
isSuccess = result;
},
error: function(result){
isSuccess = result;
}
}).done(function () {
if (isSuccess == "1") {
tr.find('.edit, .display').toggle();
alert("Successfully Saved");
location.reload();
}
else {
alert("Error. Please, check the data");
}
});
});
Now, modify the HomeController
and add InsertData
method. This is a simple method, where it checks for duplicate id. If the id is duplicate, it will return error (0) value otherwise the student
object will be added to the Students
list. As Ajax function is calling using POST
, you need to setup method attribute of the InsertData
method to HttpPost
.
[HttpPost]
public JsonResult InsertData(Models.Student inStudent)
{
String result = String.Empty;
Models.Student dup = Students.Find(p => p.ID == inStudent.ID);
if (dup == null)
{
Students.Add(inStudent);
result = "1";
}
else
{
result = "0";
}
return Json(result, JsonRequestBehavior.AllowGet);
}
6.2.2 Update Record
When user clicks on the Edit button, the row of data will be in edit mode and user can modify data. After editing data, when user clicks on the Save button, you need to call the SaveData
method of HomeController
using Ajax function. First, define the click
method of the Save button (save-btn
). Inside this function, grab the data for ID, Name and Email. Using the data, create a Student
object, which will be used to send data to the SaveData
Method (described in a later section) for the HomeController
. Now call SaveData
method of HomeController
using the Ajax function. SaveData
method will return 1
for success
and 0
for error
. Those values are going to capture in the isSuccess
variable. When the process of update is completed, Ajax function will call the done
event. If SaveData
method returns success
, then we need to go back to display mode and refresh the page. Otherwise, we will show the error
message.
$('.save-btn').on("click", function () {
var tr = $(this).parents('tr:first');
var name = tr.find("#inName").val();
var id = tr.find("#inID").val();
var email = tr.find("#inEmail").val();
var isSuccess = -1;
tr.find("#lblName").text(name);
tr.find("#lblEmail").text(email);
var Student =
{
"ID": id,
"Name": name,
"email": email
};
$.ajax({
url: '/Home/SaveData/',
data: JSON.stringify(Student),
type: 'POST',
contentType: 'application/json; charset=utf-8',
success: function (result) {
isSuccess = result;
},
error: function (result) {
isSuccess = result;
}
}).done(function () {
if (isSuccess == "1") {
tr.find('.edit, .display').toggle();
alert("Successfully Saved");
}
else {
alert("Error. Please, check the data");
}
});
});
Now, modify the HomeController
and add SaveData
method. This is a simple method, where it finds the existing Student
object using id. If the Student
object does not exist, it will return error
otherwise the student
object will be updated. As Ajax function is calling using POST
, you need to setup method attribute of the SaveData
method to HttpPost
.
[HttpPost]
public JsonResult SaveData(Models.Student student)
{
int index = 0;
String result = String.Empty;
index = Students.FindIndex(p => p.ID == student.ID);
if (index >= 0)
{
Students[index] = student;
result = "1";
}
else
result = "0";
return Json(result, JsonRequestBehavior.AllowGet);
}
6.2.3 Delete Record
When user clicks on the Delete button, the row of data will be deleted. This will call the Delete (delete-btn
) method. Inside this function, grab the data for ID, Name and Email. Using the data, create a Student
object, which will be used to send data to the DeleteData
method (described in a later section) for the HomeController
. Call DeleteData
method of HomeController
using the Ajax function. DeleteData
method will return 1
for success and 0
for error. Those values are going to capture in the isSuccess
variable. When the process of delete is completed, Ajax function will call the done
event. If DeleteData
method returns success
, then we need to refresh the page. Otherwise, we will show the error
message.
$('.delete-btn').on("click", function () {
var tr = $(this).parents('tr:first');
var name = tr.find("#inName").val();
var id = tr.find("#inID").val();
var email = tr.find("#inEmail").val();
var isSuccess = -1;
var Student =
{
"ID": id,
"Name": name,
"email": email
};
$.ajax({
url: '/Home/DeleteData/',
data: JSON.stringify(Student),
type: 'POST',
contentType: 'application/json; charset=utf-8',
success: function (result) {
isSuccess = result;
},
error: function (result) {
isSuccess = result;
}
}).done(function () {
if (isSuccess == "1") {
alert("Successfully Deleted");
location.reload();
}
else {
alert("Error. Please, check the data");
}
});
});
Modify the HomeController
and add DeleteData
method. This is a simple method, where it finds the existing Student
object using id. If the Student
object does not exist, it will return error
otherwise the student
object will be deleted. As Ajax function is calling using POST
, you need to setup method attribute of the DeleteData
method to HttpPost
.
[HttpPost]
public JsonResult DeleteData(Models.Student student)
{
String result = String.Empty;
Models.Student s = Students.Find(p => p.ID == student.ID);
if (s != null)
{
Students.Remove(s);
result = "1";
}
else
result = "0";
return Json(result, JsonRequestBehavior.AllowGet);
}
7.0 Conclusion
VC and JQuery together make it very easy to implement data editing for WebGrid. The same logic you may use to edit data in table instead of WebGrid. However, if you use table, then you need to maintain the page index from your code.