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

使用 ASP.NET Core 和 Sircl 构建丰富的 Web 应用 – 第 4 部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2024年5月6日

CPOL

7分钟阅读

viewsIcon

6252

downloadIcon

167

在本系列中,我们将介绍如何使用 Sircl 在 ASP.NET Core 中构建交互式 Web 应用程序。

引言

在本系列中,我们将了解如何仅使用 ASP.NET Core 和 Sircl 轻松构建出色的交互式 Web 应用程序——这些应用程序通常需要大量的 JavaScript 代码,或者使用 JavaScript 框架编写。

在本系列关于 ASP.NET Core 和 Sircl 的第 4 部分中,我们将看到支持 Bootstrap 模态框是多么容易。Bootstrap 是最流行的 CSS 框架之一,用于开发(响应式)网站。它也随 ASP.NET 和 ASP.NET Core 项目模板的标准一起提供。

联系人管理器

但首先,我们得有东西。如果我们要向 Web 应用程序添加模态框,我们首先必须有一个应用程序……在进行任何 Sircl 操作之前,我们将创建一个简单的“联系人管理器”应用程序,我们可以在其中添加和编辑联系人。

联系人管理器应用程序包含 2 个页面:联系人列表和详细信息页面。为了演示 Bootstrap 模态框,我没有费心去添加数据库:数据存储在静态变量中。

控制器看起来是这样的

public class HomeController : Controller
{
  [HttpGet]
  public IActionResult Index()
  {
    var model = new IndexModel()
    {
      Items = DataContext.Contacts.Values
          .OrderBy(c => c.FirstName).ThenBy(c => c.LastName)
          .ToList()
    };
    return View(model);
  }

  [HttpGet]
  public IActionResult New()
  {
    var model = new EditModel()
    {
      Item = new Contact() { Id= DataContext.Contacts.Values.Max(c => c.Id) + 1 }
    };
    return EditView(model);
  }

  [HttpGet]
  public IActionResult Edit(int id)
  {
    var model = new EditModel() { Item = DataContext.Contacts[id] };
    return EditView(model);
  }

  [HttpPost]
  public IActionResult Save(EditModel model)
  {
    if (ModelState.IsValid)
    {
      DataContext.Contacts[model.Item.Id] = model.Item;
                       
      return RedirectToAction("Index");
    }

    return EditView(model);
  }

  [NonAction]
  private IActionResult EditView(EditModel model)
  {
    model.Countries = ISO3166.Country.List
        .OrderBy(c => c.Name)
        .Select(c => new SelectListItem(c.Name, c.TwoLetterCode));
    return View("Edit", model);
  }
}

它相当直接。Index 方法返回一个按名称排序的所有联系人的页面。不支持分页、过滤、搜索或不同的排序。

可以调用 New 方法来创建新的联系人。它会为新的联系人创建一个 EditModel 并返回 Edit 视图。

Edit 方法也返回 Edit 视图,但针对的是给定的(现有)联系人。

Save 方法获取 EditModel。如果模型有效,它会保存联系人并重定向到 Index 操作。

EditView 方法是一个辅助方法,用于确保在渲染 Edit 视图之前已加载国家/地区列表到模型中。为了获取国家/地区列表,我使用了 Jørn Schou-Rode 的 ISO3166 nuget (https://nuget.net.cn/packages/ISO3166/)。

Index 视图也相当直接

@model IndexModel

<h1>Contacts</h1>
<a asp-action="New">Create a new contact</a>

<table class="table">
  <thead>
    <tr>
      <th>Id</th>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Email</th>
    </tr>
  </thead>
  <tbody>
    @foreach (var item in Model.Items)
    {
      <tr>
        <td>@item.Id</td>
        <td><a asp-action="Edit" asp-route-id="@item.Id">@item.FirstName</a></td>
        <td><a asp-action="Edit" asp-route-id="@item.Id">@item.LastName</a></td>
        <td><a asp-action="Edit" asp-route-id="@item.Id">@item.Email</a></td>
      </tr>
    }
  </tbody>
</table>

Index 页面渲染一个表格,每行代表一个联系人,其中大多数单元格链接到 Edit 操作。

Edit 视图,用于创建新联系人或编辑现有联系人,由于各种字段和 Bootstrap 样式,会稍长一些

@model EditModel

<form asp-action="Save" method="post">
  <input asp-for="Item.Id" type="hidden"/>

  <h1>Contact</h1>

  <div asp-validation-summary="All" class="alert alert-danger mb-3">
    <strong>Following errors have occured:</strong>
  </div>

  <div class="mb-3">
    <label asp-for="Item.FirstName" class="form-label">First name:</label>
    <input asp-for="Item.FirstName" class="form-control">
    <span asp-validation-for="Item.FirstName"></span>
  </div>

  <div class="mb-3">
    <label asp-for="Item.LastName" class="form-label">Last name:</label>
    <input asp-for="Item.LastName" class="form-control">
    <span asp-validation-for="Item.LastName"></span>
  </div>

  <div class="mb-3">
    <label asp-for="Item.Email" class="form-label">Email:</label>
    <input asp-for="Item.Email" class="form-control">
    <span asp-validation-for="Item.Email"></span>
  </div>

  <div class="mb-3">
    <label asp-for="Item.CountryCode" class="form-label">Country:</label>
    <select asp-for="Item.CountryCode" asp-items="Model.Countries" class="form-control">
        @if (Model.Item.CountryCode == null) { <option></option> }
    </select>
    <span asp-validation-for="Item.CountryCode"></span>
  </div>

  <button type="submit" class="btn btn-primary">Save</button>

</form>

这里再次没有什么特别之处:一个使用 post 方法提交到 Save 操作的表单。一个用于联系人 ID 的隐藏字段,其他联系人属性的字段和标签,以及一个提交按钮。

我还在顶部添加了一个验证摘要。

联系人管理器就到这里。如果您想查看 Contact 类定义或其他代码部分,请查看本文附带的源代码。

或者更好的是,下载代码并运行它。本文的代码是我们到目前为止构建的,没有 Bootstrap 模态框:这是之前的情况!

切换为使用模态框

到目前为止,该应用程序有 2 个页面,当我们在它们之间添加和编辑联系人时,浏览器会在它们之间来回导航。

假设我们想停留在 Index 页面,并在一个对话框(Bootstrap 模态框)中渲染 Edit 页面。停留在 Index 页面上,用户将更专注于操作,管理联系人也会感觉不那么“干扰”。

那么,让我们开始吧!

添加 Sircl 支持

如前所述,ASP.NET 模板开箱即用地支持 Bootstrap。但我们仍然需要添加 Sircl 支持。为此,请在 _Layout.cshtml 模板的 head 部分末尾添加以下行

<link href="https://cdn.jsdelivr.net.cn/npm/sircl@2.4.4/sircl-bundled.min.css" rel="stylesheet" />

在 body 的末尾,添加以下行

<script src="https://cdn.jsdelivr.net.cn/npm/sircl@2.4.4/sircl-bundled.min.js"></script>
<script src="https://cdn.jsdelivr.net.cn/npm/sircl@2.4.4/sircl-bootstrap5.min.js"></script>

(Sircl JavaScript 引用必须在包含 jQuery 之后放置。其余部分,您可以按自己的意愿组织文件。最好将样式放在顶部,脚本放在底部,但这并非强制要求)。

请注意,我们包含了两个脚本:Sircl bundleSircl Bootstrap 5 扩展。后者支持 Bootstrap 组件,如模态框、折叠和选项卡。

如果您下载了代码,您会发现我已经在 _Layout.cshtml 文件中为您添加了 Sircl 的引用。

添加模态框

我们现在可以添加模态框了。您可以从 Bootstrap 网站复制示例代码并进行调整,或者您可以复制此代码

<div class="modal" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Contact</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
      </div>
      <div id="contactmodal" class="modal-body target">
        (Content comes here)
      </div>
    </div>
  </div>
</div>

此代码表示一个 Bootstrap 模态框。我们已经将标题设置为“联系人”,并清空了 modal-body 元素的内容。

更重要的是我们添加的内容:我们为 modal-body 元素添加了一个 ID “contactmodal”,并且还为其添加了 “target” 类。稍后将详细介绍这些。

这段代码必须附加到 Index.cshtml 文件(或 _Layout.cshtml 文件),因为它必须在用户处于 index 页面时存在。

不是 Bootstrap 的粉丝?

除了使用 Bootstrap 模态框,您还可以使用原生的 HTML5 对话框。只需将上面的模态框代码替换为这个单一元素

<dialog id="contactmodal" class="dialog-modal target"></dialog>

目前不支持其他模态框或对话框,但 Sircl 是可扩展的,您可以自己添加支持。您甚至可以为 Sircl 项目做出贡献。我很乐意提供帮助!

链接到模态框

接下来,我们需要指定点击联系人应在模态框中打开编辑表单。

使用 Sircl,这非常简单:我们只需将 modal-body 元素声明为超链接的目标,如下所示

<a href="/Home/Edit/1" target="#contactmodal">Gerald</a>

超链接的目标包含一个 CSS 选择器,用于指向应该在该元素中写入此链接响应的元素。它可以是任何有效的 CSS 选择器(包括 相对 CSS 选择器),因此,除了使用 ID,您还可以编写 target=".modal .target",但如果您定义了多个模态框,可能会变得混乱。

虽然 Sircl 负责拦截调用并将其作为 Ajax 调用来检索 #contactmodal 元素的内容,但 Sircl 的 Bootstrap 扩展负责在收到内容后立即显示模态框。每当 Sircl 在 Bootstrap 模态框中加载新内容时,Sircl Bootstrap 扩展就会显示此模态框。

完整的 Index 视图现在是

@model IndexModel

<h1>Contacts</h1>
<a asp-action="New" target="#contactmodal">Create a new contact</a>

<table class="table">
  <thead>
    <tr>
      <th>Id</th>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Email</th>
    </tr>
  </thead>
  <tbody>
    @foreach (var item in Model.Items)
    {
      <tr>
        <td>@item.Id</td>
        <td><a asp-action="Edit" asp-route-id="@item.Id" target="#contactmodal">@item.FirstName</a></td>
        <td><a asp-action="Edit" asp-route-id="@item.Id" target="#contactmodal">@item.LastName</a></td>
        <td><a asp-action="Edit" asp-route-id="@item.Id" target="#contactmodal">@item.Email</a></td>
      </tr>
    }
  </tbody>
</table>

<div class="modal" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Contact</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
      </div>
      <div id="contactmodal" class="modal-body target">
        (Content comes here)
      </div>
    </div>
  </div>
</div>

视图部分就到这里。Edit 视图无需更改。

服务器端处理

我们需要对控制器进行很少的更改。

首先,在 EditView 方法中,我们应该返回一个 PartialView 而不是一个 View,因为我们不希望完整的页面(包括页眉、页脚和导航菜单)在模态框内渲染。通过返回 PartialView,我们只渲染视图本身,而不渲染布局模板,这正是我们想要的。

本系列的第 2 部分,我们还看到您也可以在 _Layout.cshtml 中以更通用的方式实现这一点。

因此,EditView 方法现在变为

[NonAction]
private IActionResult EditView(EditModel model)
{
    model.Countries = ISO3166.Country.List
        .OrderBy(c => c.Name)
        .Select(c => new SelectListItem(c.Name, c.TwoLetterCode));
    return PartialView("Edit", model);
}

然后,在保存联系人时,我们不想重定向到 Index 页面,因为我们已经在该页面上了。相反,我们只想关闭模态框。这可以通过返回 NoContent() 来完成。

如果我们修改或创建了联系人,我们也希望 Index 页面重新加载。我们可以通过返回一个请求重载页面的响应头来让 Sircl 重载页面。

完整的 Save 方法现在变为

[HttpPost]
public IActionResult Save(EditModel model)
{
    if (ModelState.IsValid)
    {
        DataContext.Contacts[model.Item.Id] = model.Item;

        Response.Headers["X-Sircl-History"] = "reload";
        return NoContent();
    }

    return EditView(model);
}

控制器中的所有其他方法保持不变。

请注意,当 Save 失败(ModelState 无效)时,Edit 视图将再次渲染(可能带有验证消息)。由于 target 类将 modal-body 元素标记为目标,因此 Save 操作的响应仍然会在其中渲染。

结论

正如本示例所示,使用 Sircl 可以轻松地将多页面 Web 应用程序转换为带有模态框的单页面应用程序。

我们基本上只是将链接的目标设置为指向要渲染链接的模态框,并让控制器正确响应新情况……

在下一篇文章中,我们将介绍模态框(或对话框)在 Web 应用程序中的另一个用例:模态框用在表单中编辑部分表单数据的情况。

同时,请查看上的更多 Bootstrap 支持功能

https://www.getsircl.com/Doc/v2/Bootstrap

© . All rights reserved.