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 |
|||
{ |
|||
public static class BundleContributorListExtensions |
|||
public static class BundleContributorCollectionExtensions |
|||
{ |
|||
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.Bundling; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreMvcUiBootstrapModule) |
|||
)] |
|||
typeof(AbpAspNetCoreMvcUiBootstrapModule), |
|||
typeof(AbpAspNetCoreMvcUiBundlingModule) |
|||
)] |
|||
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