mirror of https://github.com/abpframework/abp.git
22 changed files with 393 additions and 13 deletions
@ -0,0 +1,3 @@ |
|||||
|
# Dashboards |
||||
|
|
||||
|
TODO |
||||
@ -0,0 +1,84 @@ |
|||||
|
# Widgets |
||||
|
|
||||
|
ABP provides a model and infrastructure to create **reusable widgets**. It relies on [ASP.NET Core's ViewComponent](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components) system, so any view component can be used as a widget. Widget system is especially useful when you want to; |
||||
|
|
||||
|
* Define widgets in reusable **[modules](../Module-Development-Basics.md)**. |
||||
|
* Have **scripts & styles** for your widget. |
||||
|
* Create **[dashboards](Dashboards.md)** with widgets used in (widgets you've built yourself or defined by modules you are using). |
||||
|
* Co-operate widgets with **[authorization](../Authorization.md)** and **[bundling](Bundling-Minification.md)** systems. |
||||
|
|
||||
|
## Basic Widget Definition |
||||
|
|
||||
|
### Create a View Component |
||||
|
|
||||
|
As the first step, create a regular ASP.NET Core View Component: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
**MySimpleWidgetViewComponent.cs**: |
||||
|
|
||||
|
````csharp |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget |
||||
|
{ |
||||
|
public class MySimpleWidgetViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
public IViewComponentResult Invoke() |
||||
|
{ |
||||
|
return View(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
**Default.cshtml**: |
||||
|
|
||||
|
```xml |
||||
|
<div class="my-simple-widget"> |
||||
|
<h2>My Simple Widget</h2> |
||||
|
<p>This is a simple widget!</p> |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
### Define the Widget |
||||
|
|
||||
|
Second step is to define a widget using the `WidgetOptions` (in the `ConfigureServices` method of your Web module): |
||||
|
|
||||
|
````csharp |
||||
|
Configure<WidgetOptions>(options => |
||||
|
{ |
||||
|
options.Widgets.Add( |
||||
|
new WidgetDefinition( |
||||
|
"MySimpleWidget", //Unique Widget name |
||||
|
typeof(MySimpleWidgetViewComponent) //Type of the ViewComponent |
||||
|
) |
||||
|
); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
Widget name should be unique in the application. |
||||
|
|
||||
|
*TODO: This is in development and will probably change!* |
||||
|
|
||||
|
### Render the Widget |
||||
|
|
||||
|
Whenever you want to render a widget, you can inject the `IWidgetRenderer` and use the `RenderAsync` method with the unique widget name. |
||||
|
|
||||
|
````xml |
||||
|
@inject IWidgetRenderer WidgetRenderer |
||||
|
|
||||
|
@await WidgetRenderer.RenderAsync(Component, "MySimpleWidget") |
||||
|
```` |
||||
|
|
||||
|
You could do the same with standard `Component.InvokeAsync` method. The main difference is that `IWidgetRenderer` uses the widget name defined before. The essential benefit of the `WidgetRenderer` comes when your widget has additional resources, like script and style files. |
||||
|
|
||||
|
## Style & Script Dependencies |
||||
|
|
||||
|
There are some challenges when your widget has script and style files; |
||||
|
|
||||
|
* Any page uses the widget should also include the **its script & styles** files into the page. |
||||
|
* The page should also care about **depended libraries/files** of the widget. |
||||
|
|
||||
|
ABP solves all these issues when you properly relate the resources with the widget. You don't care about dependencies of the widget. |
||||
|
After Width: | Height: | Size: 9.9 KiB |
@ -1,6 +1,6 @@ |
|||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling |
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling |
||||
{ |
{ |
||||
public static class BundleContributorListExtensions |
public static class BundleContributorCollectionExtensions |
||||
{ |
{ |
||||
public static void AddFiles(this BundleContributorCollection contributors, params string[] files) |
public static void AddFiles(this BundleContributorCollection contributors, params string[] files) |
||||
{ |
{ |
||||
@ -1,13 +1,22 @@ |
|||||
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; |
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
using Volo.Abp.Modularity; |
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
||||
{ |
{ |
||||
[DependsOn( |
[DependsOn( |
||||
typeof(AbpAspNetCoreMvcUiBootstrapModule) |
typeof(AbpAspNetCoreMvcUiBootstrapModule), |
||||
)] |
typeof(AbpAspNetCoreMvcUiBundlingModule) |
||||
|
)] |
||||
public class AbpAspNetCoreMvcUiWidgetsModule : AbpModule |
public class AbpAspNetCoreMvcUiWidgetsModule : AbpModule |
||||
{ |
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<VirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpAspNetCoreMvcUiWidgetsModule>(); |
||||
|
}); |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components |
||||
|
{ |
||||
|
public class WidgetResourcesViewModel |
||||
|
{ |
||||
|
public IReadOnlyList<WidgetDefinition> Widgets { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
@model Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetResourcesViewModel |
||||
|
@if (Model.Widgets.Any()) |
||||
|
{ |
||||
|
var resourceItems = Model.Widgets.SelectMany(w => w.Scripts).ToArray(); |
||||
|
if (resourceItems.Any()) |
||||
|
{ |
||||
|
<abp-script-bundle> |
||||
|
@foreach (var resourceItem in resourceItems) |
||||
|
{ |
||||
|
if (resourceItem.Src != null) |
||||
|
{ |
||||
|
<abp-script src="@resourceItem.Src" /> |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
<abp-script type="@resourceItem.Type" /> |
||||
|
} |
||||
|
} |
||||
|
</abp-script-bundle> |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts |
||||
|
{ |
||||
|
public class WidgetScriptsViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
protected IPageWidgetManager PageWidgetManager { get; } |
||||
|
protected WidgetOptions Options { get; } |
||||
|
|
||||
|
public WidgetScriptsViewComponent( |
||||
|
IPageWidgetManager pageWidgetManager, |
||||
|
IOptions<WidgetOptions> options) |
||||
|
{ |
||||
|
PageWidgetManager = pageWidgetManager; |
||||
|
Options = options.Value; |
||||
|
} |
||||
|
|
||||
|
public virtual IViewComponentResult Invoke() |
||||
|
{ |
||||
|
return View( |
||||
|
"~/Volo/Abp/AspNetCore/Mvc/UI/Widgets/Components/WidgetScripts/Default.cshtml", |
||||
|
new WidgetResourcesViewModel |
||||
|
{ |
||||
|
Widgets = PageWidgetManager.GetAll() |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
@model Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetResourcesViewModel |
||||
|
@if (Model.Widgets.Any()) |
||||
|
{ |
||||
|
var resourceItems = Model.Widgets.SelectMany(w => w.Styles).ToArray(); |
||||
|
if (resourceItems.Any()) |
||||
|
{ |
||||
|
<abp-style-bundle> |
||||
|
@foreach (var resourceItem in resourceItems) |
||||
|
{ |
||||
|
if (resourceItem.Src != null) |
||||
|
{ |
||||
|
<abp-style src="@resourceItem.Src" /> |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
<abp-style type="@resourceItem.Type" /> |
||||
|
} |
||||
|
} |
||||
|
</abp-style-bundle> |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles |
||||
|
{ |
||||
|
public class WidgetStylesViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
protected IPageWidgetManager PageWidgetManager { get; } |
||||
|
protected WidgetOptions Options { get; } |
||||
|
|
||||
|
public WidgetStylesViewComponent( |
||||
|
IPageWidgetManager pageWidgetManager, |
||||
|
IOptions<WidgetOptions> options) |
||||
|
{ |
||||
|
PageWidgetManager = pageWidgetManager; |
||||
|
Options = options.Value; |
||||
|
} |
||||
|
|
||||
|
public virtual IViewComponentResult Invoke() |
||||
|
{ |
||||
|
return View( |
||||
|
"~/Volo/Abp/AspNetCore/Mvc/UI/Widgets/Components/WidgetStyles/Default.cshtml", |
||||
|
new WidgetResourcesViewModel |
||||
|
{ |
||||
|
Widgets = PageWidgetManager.GetAll() |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers |
||||
|
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap |
||||
|
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
||||
|
{ |
||||
|
public interface IPageWidgetManager |
||||
|
{ |
||||
|
bool TryAdd(WidgetDefinition widget); |
||||
|
|
||||
|
IReadOnlyList<WidgetDefinition> GetAll(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
||||
|
{ |
||||
|
public class PageWidgetManager : IPageWidgetManager, IScopedDependency |
||||
|
{ |
||||
|
public const string HttpContextItemName = "__AbpCurrentWidgets"; |
||||
|
|
||||
|
private readonly IHttpContextAccessor _httpContextAccessor; |
||||
|
|
||||
|
public PageWidgetManager(IHttpContextAccessor httpContextAccessor) |
||||
|
{ |
||||
|
_httpContextAccessor = httpContextAccessor; |
||||
|
} |
||||
|
|
||||
|
public bool TryAdd(WidgetDefinition widget) |
||||
|
{ |
||||
|
return GetWidgetList() |
||||
|
.AddIfNotContains(widget); |
||||
|
} |
||||
|
|
||||
|
private List<WidgetDefinition> GetWidgetList() |
||||
|
{ |
||||
|
var httpContext = _httpContextAccessor.HttpContext; |
||||
|
if (httpContext == null) |
||||
|
{ |
||||
|
throw new AbpException($"{typeof(PageWidgetManager).AssemblyQualifiedName} should be used in a web request! Can not get IHttpContextAccessor.HttpContext."); |
||||
|
} |
||||
|
|
||||
|
var widgets = httpContext.Items[HttpContextItemName] as List<WidgetDefinition>; |
||||
|
if (widgets == null) |
||||
|
{ |
||||
|
widgets = new List<WidgetDefinition>(); |
||||
|
httpContext.Items[HttpContextItemName] = widgets; |
||||
|
} |
||||
|
|
||||
|
return widgets; |
||||
|
} |
||||
|
|
||||
|
public IReadOnlyList<WidgetDefinition> GetAll() |
||||
|
{ |
||||
|
return GetWidgetList().ToImmutableArray(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
using System; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
||||
|
{ |
||||
|
public class WidgetResourceItem |
||||
|
{ |
||||
|
[CanBeNull] |
||||
|
public string Src { get; } |
||||
|
|
||||
|
[CanBeNull] |
||||
|
public Type Type { get; } |
||||
|
|
||||
|
public WidgetResourceItem([NotNull] string src) |
||||
|
{ |
||||
|
Src = Check.NotNullOrWhiteSpace(src, nameof(src)); |
||||
|
} |
||||
|
|
||||
|
public WidgetResourceItem([NotNull] Type type) |
||||
|
{ |
||||
|
Type = Check.NotNull(type, nameof(type)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue