Razor 应用程序:我希望 html 按钮显示 jQuery UI 在执行 C# 方法删除数据库注册表之前确认对话框

Razor app: I want html button shows jQuery UI confirm dialog before execute C# method to delete DB registry

我正在使用 Razor 应用学习网络开发。我有一个页面,PeopleIndex,其中 table 显示人员列表,在每一行之后,第一个 用于编辑人员数据,第二个 从我想的地方喜欢调用 jQuery UI 确认对话框(我已经在 .cshtml 中有代码),如果我单击“是”按钮,应该删除该人员注册表,这就是调用.cshtml.cs 文件中的 Delete() 方法。我明确表示此过程不是提交操作,我之所以这样说是因为我在 Internet 上找到的与我的问题相关的所有内容都是关于“form method = POST and type=submit”,并且所有这些都应该完成在另一个页面中,我只想在我的个人列表页面中执行此操作。但是,如果我错了,我会听取意见。 我附上我的 PeopleIndex Razor 页面的两个文件:

*************** PeopleIndex.cshtml *****************

@page
@model WebAppPersonas.Pages.PersonasIndexModel
@{
    Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head title="Lista de personas">
    <link rel="stylesheet" href="~/lib/jquery-ui-1.12.1.custom/jquery-ui.min.css" />
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
    <script src="~/lib/jquery-ui-1.12.1.custom/external/jquery/jquery.js"></script>
    <script src="~/lib/jquery-ui-1.12.1.custom/jquery-ui.min.js"></script>
    <script>
        $(document).ready(function () {
            // Confirmation Dialog
            $('#confirmDialog').dialog({
                autoOpen: false,
                width: 500,
                height: auto,
                modal: true,
                resizable: false,
                buttons: {
                    "Yes": function () {
                        $(".ui-dialog-buttonpane button:contains('Si')").button("disable");
                        $(".ui-dialog-buttonpane button:contains('No')").button("disable");
                        call Delete() C# method from .cs file // neither know what goes here
                        $(this).dialog("close");
                    },
                    "No": function () {
                        $(this).dialog("close");
                    }
                }
            });

            $('#deleteReg').click(function (e) {
                e.preventDefault();
                $('#confirmDialog').dialog('open');
            });
        });
    </script>
</head>
<body>
    <a asp-page="./InsertUpdate" class="btn btn-primary"> Add person </a>
    <h2> People LIst </h2>
    <table class="table">
        <thead>
            <tr>
                <th> @Html.DisplayNameFor(Model => Model.people[0].Id) </th>
                <th> @Html.DisplayNameFor(Model => Model.people[0].Name) </th>
                <th> @Html.DisplayNameFor(Model => Model.people[0].Age) </th>
                <th> @Html.DisplayNameFor(Model => Model.people[0].Email) </th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Model.people)
            {
                <tr>
                    <td> @Html.DisplayFor(modelItem => item.Id) </td>
                    <td> @Html.DisplayFor(modelItem => item.Name) </td>
                    <td> @Html.DisplayFor(modelItem => item.Age) </td>
                    <td> @Html.DisplayFor(modelItem => item.Email) </td>
                    <td> <a asp-page="./InsertUpdate" asp-route-id="@item.Id"> Update </a> | <input type="button" value="Delete" onclick="I don't know what goes here"/> </td>
                </tr>
            }
        </tbody>
    </table>

    <div id="confirmDialog" title="Confirm delete">
        <p> You are about to delete a registry ¿Are you sure?</p>
    </div>
</body>
</html>

********** PeopleIndex.cshtml.cs *****************

    public void OnGet()
    {
        people = dataAccess.GetPeople();
    }

    public ActionResult Delete(int? id) 
    {
        dataAccess.DeletePerson(id.Value);
        return Redirect("./PeopleIndex");
    }

我不得不切断 .cshtml.cs 文件的 header,因为该网站不允许我格式化该代码,因此该网站不允许我这样做post问题。我希望现在它允许我 post 这个问题。这很复杂。我认为用不同语言格式化代码的职责应该更容易,但是,...这就是... 提前谢谢你。

编辑*** 嗨,迈克尔 我用你给我的代码编辑了文件。但是我在:

中设置了一个断点

            $('.deleteButton').click(function (e) {
                e.preventDefault();
                const id = $(e.target).data("id");
                $('#confirmDialog').data("id", id);
                $('#confirmDialog').dialog('open');
            });

它不会输入来自此处按钮的代码:

    <tbody>
        @foreach (var item in Model.people)
        {
            <tr>
                <td> @Html.DisplayFor(modelItem => item.Id) </td>
                <td> @Html.DisplayFor(modelItem => item.Name) </td>
                <td> @Html.DisplayFor(modelItem => item.Age) </td>
                <td> @Html.DisplayFor(modelItem => item.Email) </td>
                <td> <a asp-page="./InsertUpdate" asp-route-id="@item.Id"> Update </a> | <button class="deleteButton" data-id="@item.Id"> Delete </button> </td>
            </tr>
        }
    </tbody>

所以它现在不工作,但至少我们现在知道问题出在哪里了。 我再次附加受影响的文件和当前更新,我没有放 .cs 因为这是唯一可以的。 1、.cshtml.cs(现在我们知道点击deleteButton并没有进入JS函数,我试过改class for name但是还是一样。)

@page
@model WebAppPersonas.Pages.PeopleIndexModel
@section Head
{
    <link rel="stylesheet" href="~/lib/jquery-ui-1.12.1.custom/jquery-ui.min.css" />
    @*<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
        <link rel="stylesheet" href="~/css/site.css" />
        <script src="~/lib/jquery-ui-1.12.1.custom/external/jquery/jquery.js"></script>*@
    <script src="~/lib/jquery-ui-1.12.1.custom/jquery-ui.min.js"></script>
    <script>
        $(document).ready(function () {
            // Confirmation Dialog
            $('#confirmDialog').dialog({
                autoOpen: false,
                width: 500,
                height: auto,
                modal: true,
                resizable: false,
                buttons: {
                    "Yes": function () {
                        $(".ui-dialog-buttonpane button:contains('Yes')").button("disable");
                        $(".ui-dialog-buttonpane button:contains('No')").button("disable");
                        DeletePerson($(this).data("id"));
                        $(this).dialog("close");
                    },
                    "No": function () {
                        $(this).dialog("close");
                    }
                }
            });

            $('.deleteButton').click(function (e) {
                e.preventDefault();
                const id = $(e.target).data("id");
                $('#confirmDialog').data("id", id);
                $('#confirmDialog').dialog('open');
            });
        });
    </script>
    <script>
        const token = document.getElementsByName('__RequestVerificationToken')[0].nodeValue;
        function DeletePerson(id) {
            const formData = new FormData();
            formData.append("id", id);
            fetch(window.location.href, {
                method: 'DELETE',
                headers: { 'XSRF-TOKEN': token },
                body: formData
            })
                .then(result => { window.location.reload(); })
                .catch(error => alert("Error sending DELETE request."))
        }
    </script>
}
<div id="confirmDialog" title="Confirm delete">
    <p> You are about to delete a registry. Are you sure? </p>
</div>

<a asp-page="./InsertUpdate" class="btn btn-primary"> Add person </a>
<h2> List of People </h2>
<table class="table">
    <thead>
        <tr>
            <th> @Html.DisplayNameFor(Model => Model.people[0].Id) </th>
            <th> @Html.DisplayNameFor(Model => Model.people[0].Name) </th>
            <th> @Html.DisplayNameFor(Model => Model.people[0].Age) </th>
            <th> @Html.DisplayNameFor(Model => Model.people[0].Email) </th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.people)
        {
            <tr>
                <td> @Html.DisplayFor(modelItem => item.Id) </td>
                <td> @Html.DisplayFor(modelItem => item.Name) </td>
                <td> @Html.DisplayFor(modelItem => item.Age) </td>
                <td> @Html.DisplayFor(modelItem => item.Email) </td>
                <td> <a asp-page="./InsertUpdate" asp-route-id="@item.Id"> Update </a> | <button class="deleteButton" data-id="@item.Id"> Delete </button> </td>
            </tr>
        }
    </tbody>
</table>
@Html.AntiForgeryToken()

2、_Layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebAppPersonas</title>
    @*<link rel="stylesheet" href="~/lib/jquery-ui-1.12.1.custom/jquery-ui.min.css" />*@
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
    <script src="~/lib/jquery-ui-1.12.1.custom/external/jquery/jquery.js"></script>
    @*<script src="~/lib/jquery-ui-1.12.1.custom/jquery-ui.min.js"></script>*@
    @RenderSection("Head", false)
</head>
<body>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">WebAppPersonas</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - WebAppPersonas - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    @*<script src="~/lib/jquery/dist/jquery.min.js"></script>*@
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

Blockquote

Razor 页面是一种服务器端技术。您不能在 javascript 中调用 C# 函数。反而 你可以提出要求。此请求将分配给您的 Razor 页面中的方法,使用 路由中间件。

为了说明这意味着我们有一个简单的 PageModel 的问题:

public class Person
{
    public int Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
}

public class PersonsModel : PageModel
{
    private static List<Person> _persons = new List<Person>
    {
        new Person{Id = 1, Firstname = "FN1", Lastname = "LN1"},
        new Person{Id = 2, Firstname = "FN2", Lastname = "LN2"},
        new Person{Id = 3, Firstname = "FN3", Lastname = "LN3"},
        new Person{Id = 4, Firstname = "FN4", Lastname = "LN4"}
    };

    public List<Person> Persons => _persons;

    public void OnGet()
    {
    }

    // Called from fetch (JavaScript)
    public IActionResult OnDelete(int id)
    {
        var person = _persons.Find(p => p.Id == id);
        if (person == null) { return NotFound(); // HTTP 404 triggers .catch() in fetch }
        _persons.Remove(person);
        return new NoContentResult();  // HTTP 204
    }
}

如您所见,OnDelete returns 没有重定向,因为服务器响应没有被解释 浏览器。是否回应此 return 值取决于您。

在我们的 Razor 视图中,我们有一个人员列表和每个人的删除按钮。

@page
@model RazorDemo.Pages.PersonsModel
@{
}
<ul>
    @foreach (var p in Model.Persons)
    {
        <li>@p.Id - @p.Firstname @p.Lastname <button onclick="deletePerson(@p.Id)">Delete</button></li>
    }
</ul>

@*Generate a hidden field with the RequestVerificationToken.*@
@Html.AntiForgeryToken()

<script>
    const token = document.getElementsByName('__RequestVerificationToken')[0].value;

    // Send a DELETE request as multipart/form-data (an ordinary HTML form)
    function deletePerson(id) {
        // Crates a HTML form with one parameter (id)
        const formData = new FormData();
        formData.append("id", id);

        fetch(window.location.href, {
            method: 'DELETE',
            headers: { 'XSRF-TOKEN': token },
            body: formData
        })
            .catch(error => alert("Error sending DELETE request."));
    }
</script>

服务器生成的 HTML 表单包含带有防伪标记的隐藏输入元素 (请求验证令牌,更多信息请见 www.learnrazorpages.com)。 我们必须使用 @Html.AntiForgeryToken().

手动创建此元素

JavaScript得到这个token的值。之后我们创建一个普通的有效负载 HTML 表单,因为我们想在服务器端使用标准模型绑定和验证。

现在我们需要做一些配置,因为我们要在 请求 header.

public void ConfigureServices(IServiceCollection services)
{
    // Other services
    services.AddAntiforgery(o => o.HeaderName = "xsrf-token");
}

现在你可以删除一个人,但是你的UI不会刷新。仅在重新加载后删除 人消失。现在你到了需要 JavaScript MVVM 框架而不是 JQuery 的地步。 一个小JavaScript框架是Knockout。一个 JavaScript MVVM 框架 根据来自您服务器的 json 值生成 html 内容。您可以发送您的人员名单 作为 JSON 发送给客户端并将其存储在数组中。当你删除一个人时,你 可以删除该数组中的那个人,模板引擎将自动更新您的视图。

Razor 页面支持 JSON 结果和不同的处理程序,因此您不需要单独的控制器。

// GET Persons?handler=AllPersons
public IActionResult OnGetAllPersons()
{
    return new JsonResult(Persons);
}

使用jQueryUI

要包含 jQuery UI 引用,您可以在主布局中定义一个部分 (_Layout.cshtml)。 默认模板使用 bootstrap 和 jQuery。要使 jQuery UI 正常工作,您必须参考 jQuery jQuery UI 附带的捆绑版本。我们还在head元素中定义了一个section,可以被 剃刀页面。

_Layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Other references (bootstrap css, ...) -->
    <script src="~/lib/jquery-ui-1.12.1.custom/external/jquery/jquery.js"></script>
    @RenderSection("Head", false)
</head>
<body>
    <!-- Your layout with RenderBody() -->
    <!-- NO REFERENCE TO JQUERY! -->
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

重要提示:删除(或注释掉)末尾对 lib/jquery/dist/jquery.min.js 的引用 body.

现在我们可以向“人员”页面添加一个对话框:

@page
@model RazorDemo.Pages.PersonsModel
@section Head
{
    <link rel="stylesheet" href="~/lib/jquery-ui-1.12.1.custom/jquery-ui.min.css" />

    <script src="~/lib/jquery-ui-1.12.1.custom/jquery-ui.min.js"></script>
    <script>
        $(document).ready(function () {
            // Confirmation Dialog
            $('#confirmDialog').dialog({
                autoOpen: false,
                width: 500,
                height: "auto",
                modal: true,
                resizable: false,
                buttons: {
                    "Yes": function () {
                        $(".ui-dialog-buttonpane button:contains('Si')").button("disable");
                        $(".ui-dialog-buttonpane button:contains('No')").button("disable");
                        // Call deletePerson with data-id of the dialog div.
                        deletePerson($(this).data("id"));
                        $(this).dialog("close");
                    },
                    "No": function () {
                        $(this).dialog("close");
                    }
                }
            });

            $('.deleteButton').click(function (e) {
                e.preventDefault();
                // We have a generic event handler for all buttons.
                // So we have to look at the event source and read the data-id attribute.
                const id = $(e.target).data("id");
                // Now we create a data attribute for <div id="confirmDialog">
                $('#confirmDialog').data("id", id);
                $('#confirmDialog').dialog('open');
            });
        });

        function deletePerson(id) {
            const token = document.getElementsByName('__RequestVerificationToken')[0].value;
            const formData = new FormData();
            formData.append("id", id);

            fetch(window.location.href, {
                method: 'DELETE',
                headers: { 'XSRF-TOKEN': token },
                body: formData
            })
                .then(result => { window.location.reload(); })  // Reloading the page. This is not AJAX, but it will do the job.
                .catch(error => alert("Error sending DELETE request."));
        }
    </script>
}

<div id="confirmDialog" title="Confirm delete">
    <p> You are about to delete a registry. Are you sure?</p>
</div>

<ul>
    @foreach (var p in Model.Persons)
    {
        <li>@p.Id - @p.Firstname @p.Lastname <button class="deleteButton" data-id="@p.Id">Delete</button></li>
    }
</ul>

@*Generate a hidden field with the RequestVerificationToken.*@
@Html.AntiForgeryToken()

您可以使用通用事件处理程序(用 .click() 在 jQuery 中定义)而不是 onClick。 因此,按钮具有 data-id 属性。在事件处理程序中,我们检索此 id 使用 $(e.target).data("id") 并将 data-id 属性动态分配给对话框 div.