WebGrid:ASP.NET MVC 5 中的数据内联编辑和删除






4.37/5 (11投票s)
如何在 WebGrid 中进行内联编辑和删除
引言
在本文中,我们将了解如何在 ASP.NET MVC 5 中的 WebGrid 中添加编辑和删除功能。
使用代码
在本文中,我使用 EntityFramework 从数据库读取数据。对于编辑和删除,我使用了内存集合。
这是我使用的数据表。
向 Controllers 文件夹添加一个控制器。为此,右键单击 Controllers 文件夹 -> 添加 -> 控制器。在对话框中选择如下所示的选项,然后单击添加。
在“添加控制器”对话框中,选择 Person 作为 Model 类。将控制器名称更改为您想要的名称。我将我的控制器命名为 Default2。单击“添加”按钮以添加控制器并生成相应的视图。
在向视图添加 WebGrid 代码之前,我将向自动生成的 Person 类添加一个 List<string> 类型的属性。
注意:不建议修改自动生成的类。我仅出于本文的目的使用此方法。
public partial class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Gender { get; set; } public string Phone { get; set; } public List<string> _genderList = new List<string>() {"Male", "Female"}; public List<string> GenderList { get { return _genderList; } set { _genderList = value; } } }
我利用 CSS 和 jQuery Toggle 函数来显示和隐藏控件。这个想法来自文章 http://www.mikesdotnetting.com/article/202/inline-editing-with-the-webgrid
。
从 Content 文件夹打开 Site.css 文件,并向其中添加以下 CSS 类
.webGrid { margin: 4px; border: 1px solid; background-color: burlywood; width: 500px; } .webGrid tr td { border: 1px solid; } .header { background-color:antiquewhite; } .altColor { background-color: darkgray; } .button { width: 50px; } span { padding-left: 4px; }
向 Scripts 文件夹添加一个 JavaScript 文件,并将其命名为 Custom.js。该文件将包含按钮的事件处理程序。将以下代码添加到文件中。
$(function() { $('.edit').hide(); $('.edit-case').on('click', function() { var tr = $(this).parents('tr:first'); tr.find('.edit, .read').toggle(); }); $('.update-case').on('click', function (e) { e.preventDefault(); var tr = $(this).parents('tr:first'); id = $(this).prop('id'); var gender = tr.find('#Gender').val(); var phone = tr.find('#PhoneNo').val(); $.ajax({ type: "POST", contentType: "application/json; charset=utf-8", url: "https://:55627/Default2/Edit", data: JSON.stringify({ "id":id,"gender": gender, "phone": phone }), dataType: "json", success: function(data) { tr.find('.edit, .read').toggle(); $('.edit').hide(); tr.find('#gender').text(data.person.Gender); tr.find('#phone').text(data.person.Phone); }, error: function (err) { alert("error"); } }); }); $('.cancel-case').on('click', function (e) { e.preventDefault(); var tr = $(this).parents('tr:first'); var id = $(this).prop('id'); tr.find('.edit, .read').toggle(); $('.edit').hide(); }); $('.delete-case').on('click', function(e) { e.preventDefault(); var tr = $(this).parents('tr:first'); id = $(this).prop('id'); $.ajax({ type: 'POST', contentType: "application/json; charset=utf-8", url: "https://:55627/Default2/Delete/" + id, dataType: "json", success: function (data) { alert('Delete success'); window.location.href = "https://:55627/Default2/Index"; }, error: function() { alert('Error occured during delete.'); } }); }); });
现在我们可以更改视图了。从 Views-> Default2 文件夹中打开 Index.cshtml 文件。删除视图中的自动生成代码,并将其更改为如下所示。
在 WebGrid 中,我只将两个字段设为可编辑。一个是 Gender(性别),另一个是 Phone number(电话号码)。Gender 是一个下拉列表,Phone number 是一个文本框。
@model IEnumerable<MVCApp.Models.Person> @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <script src="~/Scripts/Custom.js"></script> <div id=" grid"> @{ var gender = Model.Select(app => app.GenderList); var gender1 = from name in gender.First() select new SelectListItem { Value = name, Text = name }; var gridview = new WebGrid(source: Model,rowsPerPage:5); } @gridview.GetHtml(tableStyle: "webGrid", headerStyle: "header", alternatingRowStyle: "altColor", columns: gridview.Columns( gridview.Column("", style: "button", format: @<text> <button class="edit-case read" id="@item.PersonId">Edit</button> <button class="delete-case read" id="@item.PersonId">Delete</button> <button class="update-case edit" id="@item.PersonId">Update</button> <button class="cancel-case edit" id="@item.PersonId">Cancel</button> </text>), gridview.Column("Gender",canSort:false, format: @<text> <span id="gender" class="read">@item.Gender</span> @Html.DropDownList("Gender", gender1, new { @class = "edit" }) </text>), gridview.Column("FirstName"), gridview.Column("LastName"), gridview.Column("Phone", format: @<text> <span id="phone" class="read">@item.Phone</span> @Html.TextBox("PhoneNo", (string)item.Phone, new { @class = "edit" }) </text>) )) </div>
在上面的视图中,以下代码段创建了下拉列表的项。
@{ var gender = Model.Select(app => app.GenderList); var gender1 = from name in gender.First() select new SelectListItem { Value = name, Text = name }; }
由于我们使用 AJAX 调用 Edit 和 Delete 控制器操作方法,因此我们需要对它们进行一些更改。如我在文章开头提到的,更新和删除操作我不会访问数据库,下面的方法使用了内存集合。您可以添加自己的逻辑来与数据库交互以进行更新和删除操作。
请注意,这些方法返回的是 JsonResult 而不是 ActionResult。此外,这些方法缺少 [ValidateAntiForgeryToken] 属性。
[HttpPost] public JsonResult Edit(int id, string gender, string phone) { Person person = db.People.Find(id); person.Gender = gender; person.Phone = phone; return Json(new { person }, JsonRequestBehavior.AllowGet); } [HttpPost, ActionName("Delete")] public JsonResult DeleteConfirmed(int id) { List<Person> person = (List<Person>) Session["Persons"]; person = person.Where(p => p.PersonId != id).ToList(); Session["Persons"] = person; bool result = true; return Json(new {result}, JsonRequestBehavior.AllowGet ); }
对 Edit 和 Delete 方法的更改会产生级联效应,因此也需要对 Index 方法进行一些更改。像下面这样更改 Index 方法。
public ActionResult Index() { if (Session["Persons"] != null) { return View(List<Person> Session["Persons"]); } Session["Persons"] = db.People.ToList(); return View(db.People.ToList()); }
为了实现这一点,我们还需要向 Global.asax.cs 添加 Session_Start() 方法。
protected void Session_Start() { Session["Persons"] = null; }
现在我们已经准备好运行和测试代码了。运行项目并导航到 Default2 控制器的 Index 方法以查看 WebGrid。
单击 Delete 按钮删除一条记录。删除记录后,我们可以看到 WebGrid 页脚的分页详细信息已消失,如下图所示。因为我总共有 6 条记录,并将页面大小设置为 5 来演示这一点。
现在让我们尝试编辑一些记录。单击 Edit 按钮来编辑一条记录。单击 Edit 按钮后,您会看到 Gender 列变成了下拉列表,并选中了正确的选项,Phone 列变成了带有当前电话号码的文本框。
您还会看到,Update 和 Cancel 按钮出现,而 Edit 和 Delete 按钮消失。
让我们更改 Phone number 的值。
单击 Update 按钮保存更改并返回显示模式。
要检查 Cancel 是否正常工作,请单击 FirstName 为 Hanna
的记录的 Edit 按钮,并将 Gender 从 Female 改为 Male,并更改电话号码。
单击 Cancel 按钮可放弃所做的更改并返回显示模式。
让我们再次单击同一行的 Edit 按钮。令我们惊讶的是,我们看到,在取消之前已更改的 Gender 和 Phone number 字段的值,而不是在该行显示期间看到的值。
这是因为,当单击 Cancel 按钮时,它只会切换控件,但不会重置编辑控件的值。要解决此问题,需要将编辑控件的值恢复到用户更改它们之前的值。为此,请将以下方法更改为
$('.edit-case').on('click', function() { var tr = $(this).parents('tr:first'); tr.find('.edit, .read').toggle(); });
this
$('.edit-case').on('click', function() { var tr = $(this).parents('tr:first'); var gender = tr.find('#gender').text(); var phonenumber = tr.find('#phone').text(); tr.find('.edit, .read').toggle(); tr.find('#Gender').val(gender); tr.find('#PhoneNo').val(phonenumber); });
此函数读取显示模式期间显示的值,并将其设置为编辑模式期间控件的值。
刷新页面,然后再次单击同一行的 Edit 按钮。将 Gender 的值更改为 Male,将 Phone 更改,然后单击 Cancel。更改将被取消,原始数据将被显示。
再次单击同一行的 Edit 按钮,以查看它现在是否显示了正确的值。
我们看到它现在显示了正确的值。就是这样,我们完成了。
代码解释
当单击 Delete 按钮时,将执行此方法。Delete 功能的难点在于,数据需要重新加载。
$('.delete-case').on('click', function(e) { e.preventDefault(); var tr = $(this).parents('tr:first'); id = $(this).prop('id'); $.ajax({ type: 'POST', contentType: "application/json; charset=utf-8", url: "https://:55627/Default2/Delete/" + id, dataType: "json", success: function (data) { alert('Delete success'); window.location.href = "https://:55627/Default2/Index"; }, error: function() { alert('Error occured during delete.'); } }); });
此代码行将重定向到 Default2 控制器的 Index 操作方法,从而导致数据重新加载。
window.location.href = "https://:55627/Default2/Index";
单击 Update 按钮时,将执行此方法。
$('.update-case').on('click', function (e) { e.preventDefault(); var tr = $(this).parents('tr:first'); id = $(this).prop('id'); var gender = tr.find('#Gender').val(); var phone = tr.find('#PhoneNo').val(); $.ajax({ type: "POST", contentType: "application/json; charset=utf-8", url: "https://:55627/Default2/Edit", data: JSON.stringify({ "id":id,"gender": gender, "phone": phone }), dataType: "json", success: function(data) { tr.find('.edit, .read').toggle(); $('.edit').hide(); tr.find('#gender').text(data.person.Gender); tr.find('#phone').text(data.person.Phone); }, error: function (err) { alert("error"); } }); });
在此方法中,使用以下几行代码获取 Edit 控制器操作方法的输入参数
var tr = $(this).parents('tr:first'); id = $(this).prop('id'); var gender = tr.find('#Gender').val(); var phone = tr.find('#PhoneNo').val();
如果您查看 Edit 控制器操作方法,它会返回更新后的 Person 对象。AJAX 的 success
方法会获取此对象,并将更新后的值设置到 WebGrid 中相应的单元格。这些代码行完成了此操作。
success: function(data) { tr.find('.edit, .read').toggle(); $('.edit').hide(); tr.find('#gender').text(data.person.Gender); tr.find('#phone').text(data.person.Phone); }
缺点
使用此方法的内联编辑具有以下缺点。
1) 由于 Edit 和 Delete 的 POST 方法是通过 AJAX 调用的,因此无法在控制器操作方法上使用 [ValidateAntiForgeryToken] 属性。这使得这些方法容易受到 CSRF 攻击。
2) 此实现无法使用选项(ajaxUpdateContainerId
)。如果您使用该选项,在分页期间控件渲染时会出现问题。
关注点
如果在控制器操作方法上使用 [ValidateAntiForgeryToken],您将收到 "The required anti-forgery cookie "__RequestVerificationToken" is not present."
错误消息。
历史
07/06/2015 - 初版