小部件
ABP 提供了一个模型和基础设施来创建可重用的小部件。小部件系统是对 ASP.NET Core 的视图组件 的扩展。当您希望实现以下目标时,小部件特别有用:
基本小部件定义
创建视图组件
作为第一步,创建一个新的常规 ASP.NET Core 视图组件:
MySimpleWidgetViewComponent.cs:
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
从 AbpViewComponent 继承不是必需的。您可以继承 ASP.NET Core 标准的 ViewComponent。AbpViewComponent 只是定义了一些基础有用的属性。
您可以在 Invoke 方法中注入服务并调用,以从服务获取一些数据。您可能需要将 Invoke 方法设置为异步,例如 public async Task<IViewComponentResult> InvokeAsync()。有关所有不同用法,请参阅 ASP.NET Core 的视图组件 文档。
Default.cshtml:
<div class="my-simple-widget">
<h2>我的简单小部件</h2>
<p>这是一个简单的小部件!</p>
</div>
定义小部件
向 MySimpleWidgetViewComponent 类添加 Widget 特性,以将此视图组件标记为一个小部件:
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
渲染小部件
渲染小部件是相当标准的操作。像处理任何视图组件一样,在 Razor 视图/页面中使用 Component.InvokeAsync 方法。示例:
@await Component.InvokeAsync("MySimpleWidget")
@await Component.InvokeAsync(typeof(MySimpleWidgetViewComponent))
第一种方法使用小部件名称,而第二种方法使用视图组件类型。
带参数的小部件
ASP.NET Core 的视图组件系统允许您为视图组件接受参数。下面的示例视图组件接受 startDate 和 endDate,并使用这些参数从服务检索数据。
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Shared.Components.CountersWidget
{
[Widget]
public class CountersWidgetViewComponent : AbpViewComponent
{
private readonly IDashboardAppService _dashboardAppService;
public CountersWidgetViewComponent(IDashboardAppService dashboardAppService)
{
_dashboardAppService = dashboardAppService;
}
public async Task<IViewComponentResult> InvokeAsync(
DateTime startDate, DateTime endDate)
{
var result = await _dashboardAppService.GetCountersWidgetAsync(
new CountersWidgetInputDto
{
StartDate = startDate,
EndDate = endDate
}
);
return View(result);
}
}
}
现在,您需要传递一个匿名对象来传递参数,如下所示:
@await Component.InvokeAsync("CountersWidget", new
{
startDate = DateTime.Now.Subtract(TimeSpan.FromDays(7)),
endDate = DateTime.Now
})
小部件名称
视图组件的默认名称是基于视图组件类型的名称计算的。如果您的视图组件类型是 MySimpleWidgetViewComponent,那么小部件名称将是 MySimpleWidget(移除 ViewComponent 后缀)。这是 ASP.NET Core 计算视图组件名称的方式。
要自定义小部件的名称,只需使用 ASP.NET Core 的标准 ViewComponent 特性:
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget]
[ViewComponent(Name = "MyCustomNamedWidget")]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View("~/Pages/Components/MySimpleWidget/Default.cshtml");
}
}
}
ABP 将通过处理小部件来尊重自定义名称。
如果视图组件名称与视图组件的文件夹名称不匹配,您可能需要像本示例中那样手动编写视图路径。
显示名称
您还可以为小部件定义一个人类可读的、可本地化的显示名称。这个显示名称可以在需要时在 UI 上使用。显示名称是可选的,可以使用 Widget 特性的属性来定义:
using DashboardDemo.Localization;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(
DisplayName = "MySimpleWidgetDisplayName", //本地化键
DisplayNameResource = typeof(DashboardDemoResource) //本地化资源
)]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
有关本地化资源和键的更多信息,请参阅 本地化文档。
样式和脚本依赖
当您的小部件拥有脚本和样式文件时,会遇到一些挑战:
- 任何使用该小部件的页面都应将其脚本和样式文件也包含到页面中。
- 页面还应关心小部件的依赖库/文件。
当您将资源与小部件正确关联时,ABP 可以解决这些问题。在使用小部件时,您无需关心其依赖项。
定义为简单文件路径
下面的示例小部件添加了一个样式文件和一个脚本文件:
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(
StyleFiles = new[] { "/Pages/Components/MySimpleWidget/Default.css" },
ScriptFiles = new[] { "/Pages/Components/MySimpleWidget/Default.js" }
)]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
当您使用小部件时,ABP 会考虑这些依赖项并将其正确添加到视图/页面。样式/脚本文件可以是物理的或虚拟的。它与 虚拟文件系统 完全集成。
定义捆绑贡献者
页面中使用的小部件的所有资源都作为捆绑包添加(在生产环境中,如果没有其他配置,则会进行捆绑和压缩)。除了添加简单文件外,您还可以充分利用捆绑贡献者的强大功能。
下面的示例代码与上面的代码功能相同,但定义并使用了捆绑贡献者:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(
StyleTypes = new []{ typeof(MySimpleWidgetStyleBundleContributor) },
ScriptTypes = new[]{ typeof(MySimpleWidgetScriptBundleContributor) }
)]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
public class MySimpleWidgetStyleBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files
.AddIfNotContains("/Pages/Components/MySimpleWidget/Default.css");
}
}
public class MySimpleWidgetScriptBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files
.AddIfNotContains("/Pages/Components/MySimpleWidget/Default.js");
}
}
}
捆绑贡献系统非常强大。如果您的组件使用一个 JavaScript 库来渲染图表,那么您可以将其声明为一个依赖项,这样如果该 JavaScript 库之前没有被添加,它会自动添加到页面中。这样,使用您的组件的页面就无需关心依赖关系。
有关该系统的更多信息,请参阅 捆绑与压缩 文档。
RefreshUrl
小部件可以设计一个 RefreshUrl,每当小部件需要刷新时就会使用它。如果定义了此属性,小部件会在每次刷新时在服务器端重新渲染(参见下面的 WidgetManager 的刷新 方法)。
[Widget(RefreshUrl = "Widgets/Counters")]
public class CountersWidgetViewComponent : AbpViewComponent
{
}
一旦您为小部件定义了 RefreshUrl,就需要提供一个端点来渲染并返回它:
[Route("Widgets")]
public class CountersWidgetController : AbpController
{
[HttpGet]
[Route("Counters")]
public IActionResult Counters(DateTime startDate, DateTime endDate)
{
return ViewComponent("CountersWidget", new {startDate, endDate});
}
}
Widgets/Counters 路由与之前声明的 RefreshUrl 匹配。
小部件可以通过两种方式刷新:第一种方式,当您使用
RefreshUrl时,它在服务器上重新渲染,并被服务器返回的 HTML 替换。第二种方式,小部件从服务器获取数据(通常是 JSON 对象)并在客户端自行刷新(请参阅小部件 JavaScript API 部分中的刷新方法)。
AutoInitialize
WidgetAttribute 有一个 AutoInitialize 属性(bool 类型),可以设置为在页面准备就绪时以及每当小部件添加到 DOM 时自动初始化小部件。默认值为 false。
如果一个小部件被配置为自动初始化,则会自动为其实例创建并初始化一个 WidgetManager(见下文)。当小部件实例不分组且独立工作时(它们不需要一起初始化或刷新),这非常有用。
将 AutoInitialize 设置为 true 相当于自己编写这样的代码:
$('.abp-widget-wrapper[data-widget-name="MySimpleWidget"]')
.each(function () {
var widgetManager = new abp.WidgetManager({
wrapper: $(this),
});
widgetManager.init($(this));
});
AutoInitialize也支持通过 AJAX 加载/刷新(稍后添加到 DOM)和/或以嵌套方式使用(一个小部件在另一个小部件内部)的小部件。如果您不需要分组多个小部件并通过单个WidgetManager控制,AutoInitialize是推荐的方法。
JavaScript API
小部件可能需要在客户端渲染和刷新。在这种情况下,您可以使用 ABP 的 WidgetManager 并为小部件定义 API。
WidgetManager
WidgetManager 用于初始化和刷新一个或多个小部件。如下所示创建一个新的 WidgetManager:
$(function() {
var myWidgetManager = new abp.WidgetManager('#MyDashboardWidgetsArea');
})
MyDashboardWidgetsArea 内部可能包含一个或多个小部件。
在 document.ready 内部使用
WidgetManager(如上所示)是一个好习惯,因为其函数使用 DOM 并且需要 DOM 准备就绪。
WidgetManager.init()
init 方法简单地初始化 WidgetManager,并调用相关小部件的 init 方法(如果它们定义了,请参见下面的小部件 JavaScript API 部分)。
myWidgetManager.init();
WidgetManager.refresh()
refresh 方法刷新与此 WidgetManager 相关的所有小部件:
myWidgetManager.refresh();
WidgetManager 选项
WidgetManager 有一些额外的选项。
筛选表单
如果小部件需要参数/筛选器,那么通常会有一个表单来筛选小部件。在这种情况下,您可以创建一个包含一些表单元素的表单,以及一个包含一些小部件的仪表板区域。示例:
<form method="get" id="MyDashboardFilterForm">
...表单元素
</form>
<div id="MyDashboardWidgetsArea" data-widget-filter="#MyDashboardFilterForm">
...小部件
</div>
data-widget-filter 属性将表单与小部件关联起来。每当表单提交时,所有小部件都会自动以表单字段作为筛选条件进行刷新。
除了 data-widget-filter 属性,您还可以使用 WidgetManager 构造函数的 filterForm 参数。示例:
var myWidgetManager = new abp.WidgetManager({
wrapper: '#MyDashboardWidgetsArea',
filterForm: '#MyDashboardFilterForm'
});
筛选回调
您可能希望在初始化和刷新小部件时更好地控制筛选条件的提供。在这种情况下,您可以使用 filterCallback 选项:
var myWidgetManager = new abp.WidgetManager({
wrapper: '#MyDashboardWidgetsArea',
filterCallback: function() {
return $('#MyDashboardFilterForm').serializeFormToObject();
}
});
这个例子展示了 filterCallback 的默认实现。您可以返回任何包含字段的 JavaScript 对象。示例:
filterCallback: function() {
return {
'startDate': $('#StartDateInput').val(),
'endDate': $('#EndDateInput').val()
};
}
返回的筛选器会在 init 和 refresh 时传递给所有小部件。
小部件 JavaScript API
小部件可以定义一个 JavaScript API,供 WidgetManager 在需要时调用。下面的代码示例可以用来开始为小部件定义 API。
(function () {
abp.widgets.NewUserStatisticWidget = function ($wrapper) {
var getFilters = function () {
return {
...
};
}
var refresh = function (filters) {
...
};
var init = function (filters) {
...
};
return {
getFilters: getFilters,
init: init,
refresh: refresh
};
};
})();
这里的 NewUserStatisticWidget 是小部件的名称。它应与服务器端定义的小部件名称匹配。所有函数都是可选的。
getFilters
如果小部件有内部自定义筛选器,此函数应返回筛选器对象。示例:
var getFilters = function() {
return {
frequency: $wrapper.find('.frequency-filter option:selected').val()
};
}
此方法由 WidgetManager 在构建筛选器时使用。
init
用于在需要时初始化小部件。它有一个筛选器参数,在从服务器获取数据时可以使用。当调用 WidgetManager.init() 函数时,会使用 init 方法。如果小部件在刷新时需要完全重新加载,也会调用它。请参阅 RefreshUrl 小部件选项。
refresh
用于在需要时刷新小部件。它有一个筛选器参数,在从服务器获取数据时可以使用。每当调用 WidgetManager.refresh() 函数时,都会使用 refresh 方法。
授权
某些小部件可能只对经过身份验证或授权的用户可用。在这种情况下,请使用 Widget 特性的以下属性:
RequiresAuthentication(bool):设置为 true 可使此小部件仅对已通过身份验证的用户(用户已登录到应用程序)可用。RequiredPolicies(List<string>):授权用户所需的策略名称列表。有关策略的更多信息,请参阅 授权文档。
示例:
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(RequiredPolicies = new[] { "MyPolicyName" })]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
WidgetOptions
作为 Widget 特性的替代方案,您可以使用 AbpWidgetOptions 来配置小部件:
Configure<AbpWidgetOptions>(options =>
{
options.Widgets.Add<MySimpleWidgetViewComponent>();
});
将此代码写入您的 模块 的 ConfigureServices 方法中。使用 Widget 特性完成的所有配置也可以通过 AbpWidgetOptions 实现。为小部件添加样式的配置示例:
Configure<AbpWidgetOptions>(options =>
{
options.Widgets
.Add<MySimpleWidgetViewComponent>()
.WithStyles("/Pages/Components/MySimpleWidget/Default.css");
});
提示:
AbpWidgetOptions也可以用来获取现有小部件并更改其配置。当您想要修改应用程序使用的模块中的小部件配置时,这特别有用。使用options.Widgets.Find来获取现有的WidgetDefinition。
抠丁客



