65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.37/5 (11投票s)

2015年7月7日

CPOL

5分钟阅读

viewsIcon

108347

如何在 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 - 初版

© . All rights reserved.