ASP.NET Core MVC / Razor Pages UI: 模态框
虽然您可以继续在应用程序中使用标准的 Bootstrap 方式 来创建、打开和管理模态框,但 ABP 提供了一种灵活的方式来管理模态框,为您自动化常见任务。
示例:一个用于创建新角色实体的模态对话框
ABP 为这种包含表单的模态框提供了以下好处:
- 延迟加载 模态框 HTML 到页面,并在关闭后将其移除出 DOM。这使得使用可重用的模态对话框变得容易。此外,每次打开模态框时,它都将是一个全新的模态框,因此您无需处理重置模态框内容。
- 自动聚焦 模态框打开后表单的第一个输入框。您也可以使用
function或jquery selector来指定它。 - 自动识别模态框内的表单,并通过 AJAX 而非普通页面回发来提交表单。
- 自动检查模态框内的表单是否已更改但未保存。在这种情况下会警告用户。
- 在 AJAX 操作完成之前,自动禁用模态框按钮(保存和取消)。
- 使得注册一个模态框加载后初始化的 JavaScript 对象变得容易。
因此,在处理模态框时,尤其是在处理包含表单的模态框时,它让您编写更少的代码。
基本用法
将模态框创建为 Razor 页面
为了演示用法,我们在 /Pages/Products 文件夹下创建一个名为 ProductInfoModal.cshtml 的简单 Razor 页面:
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();
});
生成的模态框将如下所示:
传递参数打开模态框
当您调用 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 页面:
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-modal被form包裹。这是将Save和Cancel按钮放入表单所必需的。这样,Save按钮就充当了form的submit按钮。- 使用 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();
});
因此,完整的代码将类似如下(假设您在视图端有一个 id 为 OpenProductCreateModal 的 button):
$(function () {
var productCreateModal = new abp.ModalManager({
viewUrl: '/Products/ProductCreateModal'
});
$('#OpenProductCreateModal').click(function () {
productCreateModal.open();
});
});
生成的模态框将如下所示:
保存模态框
当您点击 Save 按钮时,表单将被提交到服务器。如果服务器返回成功响应,则会触发 onResult 事件,附带包含服务器响应在内的参数,并且模态框会自动关闭。
一个记录传递给 onResult 方法参数的示例回调:
productCreateModal.onResult(function(){
console.log(arguments);
});
如果服务器返回失败响应,它会显示服务器返回的错误消息,并保持模态框打开。
有关其他模态框事件,请参阅下面的 Modal Manager Reference 部分。
取消模态框
如果您在表单已更改但未保存的情况下点击 Cancel 按钮,会收到如下警告消息:
如果您不希望进行此类检查并显示消息,可以向 form 元素添加 data-check-form-on-close="false" 属性。示例:
<form method="post"
action="@Url.Page("/Products/ProductCreateModal")"
data-check-form-on-close="false">
表单验证
当您点击 Save 按钮或在表单上按 Enter 键时,ModalManager 会自动触发表单验证:
有关验证的更多信息,请参阅 表单与验证文档。
包含脚本文件的模态框
您可能需要对模态框执行一些逻辑。为此,创建一个如下所示的 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 元素(具有modalCSS 类的 HTML 元素),因此您可以对其执行任何 JQuery 方法。getForm():以 JQuery 选择器的形式返回formHTML 元素,因此您可以对其执行任何 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方法的所有参数都会传递给回调。
抠丁客









