项目

ASP.NET Core MVC / Razor Pages UI: 模态框

虽然您可以继续在应用程序中使用标准的 Bootstrap 方式 来创建、打开和管理模态框,但 ABP 提供了一种灵活的方式来管理模态框,为您自动化常见任务

示例:一个用于创建新角色实体的模态对话框

modal-manager-example-modal

ABP 为这种包含表单的模态框提供了以下好处:

  • 延迟加载 模态框 HTML 到页面,并在关闭后将其移除出 DOM。这使得使用可重用的模态对话框变得容易。此外,每次打开模态框时,它都将是一个全新的模态框,因此您无需处理重置模态框内容。
  • 自动聚焦 模态框打开后表单的第一个输入框。您也可以使用 functionjquery selector 来指定它。
  • 自动识别模态框内的表单,并通过 AJAX 而非普通页面回发来提交表单。
  • 自动检查模态框内的表单是否已更改但未保存。在这种情况下会警告用户。
  • 在 AJAX 操作完成之前,自动禁用模态框按钮(保存和取消)。
  • 使得注册一个模态框加载后初始化的 JavaScript 对象变得容易。

因此,在处理模态框时,尤其是在处理包含表单的模态框时,它让您编写更少的代码。

基本用法

将模态框创建为 Razor 页面

为了演示用法,我们在 /Pages/Products 文件夹下创建一个名为 ProductInfoModal.cshtml 的简单 Razor 页面:

modal-page-on-rider

ProductInfoModal.cshtml 内容:

@page
@model MyProject.Web.Pages.Products.ProductInfoModalModel
@{
    Layout = null;
}
<abp-modal>
    <abp-modal-header title="产品信息"></abp-modal-header>
    <abp-modal-body>
        <h3>@Model.ProductName</h3>
        <div>
            <img src="https://koudingke.oss-cn-hangzhou.aliyuncs.com/docs/abp-docs/zh-Hans/framework/ui/mvc-razor-pages/@Model.ProductImageUrl"/>
        </div>
        <p>
            @Model.ProductDescription
        </p>
        <p>
            <small><i>参考:https://acme.com/catalog/</i></small>
        </p>
    </abp-modal-body>
    <abp-modal-footer buttons="关闭"></abp-modal-footer>
</abp-modal>
  • 此页面将 Layout 设置为 null,因为我们将以模态框形式显示它。因此,不需要用布局包裹。
  • 它使用 abp-modal 标签助手 来简化模态框 HTML 代码的创建。如果您愿意,可以使用标准的 Bootstrap 模态框代码。

ProductInfoModalModel.cshtml.cs 内容:

using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;

namespace MyProject.Web.Pages.Products
{
    public class ProductInfoModalModel : AbpPageModel
    {
        public string ProductName { get; set; }

        public string ProductDescription { get; set; }

        public string ProductImageUrl { get; set; }

        public void OnGet()
        {
            ProductName = "Acme Indestructo Steel Ball";
            ProductDescription = "ACME Indestructo Steel Ball 是完全坚不可摧的,没有什么能摧毁它!";
            ProductImageUrl = "https://acme.com/catalog/acmeindestructo.jpg";
        }
    }
}

当然,您可以从数据库或 API 获取产品信息。为了简单起见,我们在这里硬编码设置属性。

定义模态管理器

一旦有了模态框,就可以在任意页面使用简单的 JavaScript 代码打开它。

首先,在将使用该模态框的页面的 JavaScript 文件中,通过设置 viewUrl 来创建一个 abp.ModalManager 对象:

var productInfoModal = new abp.ModalManager({
    viewUrl: '/Products/ProductInfoModal'
});

如果只需要指定 viewUrl,可以直接将其传递给 ModalManager 构造函数,作为一种快捷方式。例如:new abp.ModalManager('/Products/ProductInfoModal');

打开模态框

然后在需要时打开模态框:

productInfoModal.open();

通常,您希望在某些事情发生时打开模态框;例如,当用户点击按钮时:

$('#OpenProductInfoModal').click(function(){
    productInfoModal.open();
});

生成的模态框将如下所示:

modal-example-product-info

传递参数打开模态框

当您调用 open() 方法时,ModalManager 通过从 viewUrl 请求来加载模态框 HTML。您可以在打开模态框时向此 URL 传递一些查询字符串参数

示例:打开模态框时传递产品 ID

productInfoModal.open({
    productId: 42
});

您可以向 Get 方法添加一个 productId 参数:

using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;

namespace MyProject.Web.Pages.Products
{
    public class ProductInfoModalModel : AbpPageModel
    {
        //...

        public async Task OnGetAsync(int productId) //添加 productId 参数
        {
            //TODO: 根据给定的 productId 从数据库获取产品
            //...
        }
    }
}

这样,您就可以使用 productId 从数据源查询产品。

包含表单的模态框

当您想在模态框内使用表单时,abp.ModalManager 会处理各种常见任务(在简介中描述过)。

包含表单的示例模态框

本节展示一个创建新产品的表单示例。

创建 Razor 页面

为此示例,在 /Pages/Products 文件夹下创建一个名为 ProductCreateModal.cshtml 的新 Razor 页面:

product-create-modal-page-on-rider

ProductCreateModal.cshtml 内容:

@page
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model MyProject.Web.Pages.Products.ProductCreateModalModel
@{
    Layout = null;
}
<form method="post" action="@Url.Page("/Products/ProductCreateModal")">
    <abp-modal>
        <abp-modal-header title="创建新产品"></abp-modal-header>
        <abp-modal-body>
            <abp-input asp-for="Product.Name"/>
            <abp-input asp-for="Product.Description"/>
            <abp-input asp-for="Product.ReleaseDate"/>
        </abp-modal-body>
        <abp-modal-footer buttons="@AbpModalButtons.Save | @AbpModalButtons.Cancel"></abp-modal-footer>
    </abp-modal>
</form>
  • abp-modalform 包裹。这是将 SaveCancel 按钮放入表单所必需的。这样,Save 按钮就充当了 formsubmit 按钮。
  • 使用 abp-input 标签助手 来简化表单元素的创建。否则,您需要编写更多的 HTML。

ProductCreateModal.cshtml.cs 内容:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;

namespace MyProject.Web.Pages.Products
{
    public class ProductCreateModalModel : AbpPageModel
    {
        [BindProperty]
        public PoductCreationDto Product { get; set; }

        public async Task OnGetAsync()
        {
            //TODO: 获取逻辑,如果有的话
        }

        public async Task<IActionResult> OnPostAsync()
        {
            //TODO: 保存产品...

            return NoContent();
        }
    }
}
  • 这是一个简单的 PageModal 类。[BindProperty] 使得在提交表单时,表单绑定到模型;这是标准的 ASP.NET Core 机制。
  • OnPostAsync 返回 NoContent(此方法由基类 AbpPageModel 定义)。因为在表单提交操作后,我们在客户端不需要返回值。

PoductCreationDto:

ProductCreateModalModel 使用如下定义的 PoductCreationDto 类:

using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;

namespace MyProject.Web.Pages.Products
{
    public class PoductCreationDto
    {
        [Required]
        [StringLength(128)]
        public string Name { get; set; }
        
        [TextArea(Rows = 4)]
        [StringLength(2000)]
        public string Description { get; set; }
        
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
    }
}
  • abp-input 标签助手能够理解数据注解属性,并使用它们来塑造和验证表单元素。有关更多信息,请参阅 abp-input 标签助手 文档。

定义模态管理器

再次,在将使用该模态框的页面的 JavaScript 文件中,通过设置 viewUrl 来创建一个 abp.ModalManager 对象:

var productCreateModal = new abp.ModalManager({
    viewUrl: '/Products/ProductCreateModal'
});

打开模态框

然后在需要时打开模态框:

productCreateModal.open();

通常,您希望在某些事情发生时打开模态框;例如,当用户点击按钮时:

$('#OpenProductCreateModal').click(function(){
    productCreateModal.open();
});

因此,完整的代码将类似如下(假设您在视图端有一个 idOpenProductCreateModalbutton):

$(function () {

    var productCreateModal = new abp.ModalManager({
        viewUrl: '/Products/ProductCreateModal'
    });

    $('#OpenProductCreateModal').click(function () {
        productCreateModal.open();
    });

});

生成的模态框将如下所示:

modal-example-product-create

保存模态框

当您点击 Save 按钮时,表单将被提交到服务器。如果服务器返回成功响应,则会触发 onResult 事件,附带包含服务器响应在内的参数,并且模态框会自动关闭。

一个记录传递给 onResult 方法参数的示例回调:

productCreateModal.onResult(function(){
   console.log(arguments);
});

如果服务器返回失败响应,它会显示服务器返回的错误消息,并保持模态框打开。

有关其他模态框事件,请参阅下面的 Modal Manager Reference 部分。

取消模态框

如果您在表单已更改但未保存的情况下点击 Cancel 按钮,会收到如下警告消息:

modal-manager-cancel-warning

如果您不希望进行此类检查并显示消息,可以向 form 元素添加 data-check-form-on-close="false" 属性。示例:

<form method="post"
      action="@Url.Page("/Products/ProductCreateModal")"
      data-check-form-on-close="false">

表单验证

当您点击 Save 按钮或在表单上按 Enter 键时,ModalManager 会自动触发表单验证:

modal-manager-validation

有关验证的更多信息,请参阅 表单与验证文档

包含脚本文件的模态框

您可能需要对模态框执行一些逻辑。为此,创建一个如下所示的 JavaScript 文件:

abp.modals.ProductInfo = function () {

    function initModal(modalManager, args) {
        var $modal = modalManager.getModal();
        var $form = modalManager.getForm();

        $modal.find('h3').css('color', 'red');
        
        console.log('模态框已初始化...');
    };

    return {
        initModal: initModal
    };
};
  • 此代码简单地在 abp.modals 命名空间中添加了一个 ProductInfo 类。ProductInfo 类公开了一个公共函数:initModal
  • initModal 方法由 ModalManager 在模态框 HTML 插入 DOM 并准备好进行初始化逻辑后调用。
  • modalManager 参数是与该模态框实例相关的 ModalManager 对象。因此,您可以在代码中使用其上的任何函数。请参阅 ModalManager Reference 部分。

然后将此文件包含到使用该模态框的页面中:

<abp-script src="/Pages/Products/ProductInfoModal.js"/>
<abp-script src="/Pages/Products/Index.js"/>
  • 我们在这里使用了 abp-script 标签助手。如果您想了解它,请参阅 捆绑与压缩 文档。您也可以使用标准的 script 标签。对于这种情况,这并不重要。

最后,在创建 ModalManager 实例时设置 modalClass 选项:

var productInfoModal = new abp.ModalManager({
    viewUrl: '/Products/ProductInfoModal',
    modalClass: 'ProductInfo' //与 abp.modals.ProductInfo 匹配
});

延迟加载脚本文件

与其将 ProductInfoModal.js 添加到使用模态框的页面,您可以将其配置为在首次打开模态框时延迟加载脚本文件。

示例:

var productInfoModal = new abp.ModalManager({
    viewUrl: '/Products/ProductInfoModal',
    scriptUrl: '/Pages/Products/ProductInfoModal.js', //延迟加载的 URL
    modalClass: 'ProductInfo'
});
  • scriptUrl 用于设置加载模态框脚本文件的 URL。
  • 在这种情况下,您不再需要将 ProductInfoModal.js 包含到页面中。它将按需加载。

提示:捆绑与压缩

虽然延迟加载一开始看起来很酷,但它需要在您首次打开模态框时向服务器发出额外请求。

相反,您可以使用 捆绑与压缩 系统为页面使用的所有脚本文件创建一个捆绑包(在生产环境中是一个单一的压缩文件):

<abp-script-bundle>
    <abp-script src="/Pages/Products/ProductInfoModal.js"/>
    <abp-script src="/Pages/Products/Index.js"/>
</abp-script-bundle>

如果脚本文件不大,并且用户在页面使用过程中频繁打开模态框,这种方式是高效的。

或者,如果模态框仅始终在同一页面中使用,您可以在页面的主 JavaScript 文件中定义 abp.modals.ProductInfo 类。在这种情况下,您完全不需要另一个外部脚本文件。

ModalManager 参考

选项

创建新的 ModalManager 对象时可以传递选项:

var productInfoModal = new abp.ModalManager({
    viewUrl: '/Products/ProductInfoModal',
    //...其他选项
});

以下是所有可用选项的列表:

  • viewUrl(必需,string):延迟加载模态框 HTML 的 URL。
  • scriptUrl(可选,string):延迟加载 JavaScript 文件的 URL。仅在模态框首次打开时加载一次。
  • modalClass(可选,string):在 abp.modals 命名空间中定义的 JavaScript 类,可用于执行与模态框相关的代码。
  • focusElement(可选,function or string):指定获得焦点的元素。

函数

创建新的 ModalManager 对象后,可以使用其函数对模态框执行操作。示例:

var myModal = new abp.ModalManager({
    //...选项
});

//打开模态框
myModal.open();

//关闭模态框
myModal.close();

以下是 ModalManager 对象所有可用函数的列表:

  • open([args]):打开模态对话框。它可以接收一个 args 对象,该对象在从服务器获取 viewUrl 时会被转换为查询字符串。例如,如果 args{ productId: 42 },那么 ModalManager 在从服务器加载视图时会在 viewUrl 末尾附加 ?productId=42
  • reopen():使用最近一次为 open() 方法提供的 args 重新打开模态框。因此,如果您想用相同的 args 重新打开模态框,这是一个快捷方式。
  • close():关闭模态框。模态框 HTML 在关闭后会自动从 DOM 中移除。
  • getModalId():获取包含服务器返回视图的容器的 id 属性。这是每个模态框的唯一 ID,创建 ModalManager 后不会更改。
  • getModal():以 JQuery 选择器的形式返回模态框包装 DOM 元素(具有 modal CSS 类的 HTML 元素),因此您可以对其执行任何 JQuery 方法。
  • getForm():以 JQuery 选择器的形式返回 form HTML 元素,因此您可以对其执行任何 JQuery 方法。如果模态框内没有表单,则返回 null
  • getArgs():获取最近一次打开模态框时提供的参数对象。
  • getOptions():获取传递给 ModalManager 构造函数的选项对象。
  • setResult(...):使用提供的参数触发 onResult 事件。可以传递零个或多个直接传递给 onResult 事件的参数。此函数通常由模态框脚本调用,以通知使用模态框的页面。

事件

创建新的 ModalManager 对象后,可以使用其函数注册模态框的事件。示例:

var myModal = new abp.ModalManager({
    //...选项
});

myModal.onOpen(function () {
    console.log('模态框已打开...');
});

myModal.onClose(function () {
    console.log('模态框已关闭...');
});

以下是注册 ModalManager 对象事件的所有可用函数的列表:

  • onOpen(callback):注册一个回调函数,以便在模态框打开时收到通知。它在模态框完全显示在 UI 上时触发。
  • onClose(callback):注册一个回调函数,以便在模态框关闭时收到通知。它在模态框完全从 UI 上消失时触发。
  • onResult(callback):注册一个回调函数,当调用 setResult(...) 方法时触发。传递给 setResult 方法的所有参数都会传递给回调。
在本文档中