You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
abp/docs/en/AspNetCore/Widgets.md

9.3 KiB

Widgets

ABP provides a model and infrastructure to create reusable widgets. Widget system is an extension to ASP.NET Core's ViewComponents. Widgets are especially useful when you want to;

  • Define widgets in reusable modules.
  • Have scripts & styles dependencies for your widget.
  • Create dashboards with widgets used inside.
  • Co-operate widgets with authorization and bundling systems.

Basic Widget Definition

Create a View Component

As the first step, create a new regular ASP.NET Core View Component:

widget-basic-files

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();
        }
    }
}

Inheriting from AbpViewComponent is not required. You could inherit from ASP.NET Core's standard ViewComponent. AbpViewComponent only defines some base useful properties.

Default.cshtml:

<div class="my-simple-widget">
    <h2>My Simple Widget</h2>
    <p>This is a simple widget!</p>
</div>

Define the Widget

Add a Widget attribute to the MySimpleWidgetViewComponent class to mark this view component as a 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();
        }
    }
}

Rendering a Widget

Rendering a widget is pretty standard. Use the Component.InvokeAsync method in a razor view/page as you do for any view component. Examples:

@await Component.InvokeAsync("MySimpleWidget")
@await Component.InvokeAsync(typeof(MySimpleWidgetViewComponent))

First approach uses the widget name while second approach uses the view component type.

Widget Name

Default name of the view components are calculated based on the name of the view component type. If your view component type is MySimpleWidgetViewComponent then the widget name will be MySimpleWidget (removes ViewComponent postfix). This is how ASP.NET Core calculates a view component's name.

To customize widget's name, just use the standard ViewComponent attribute of ASP.NET Core:

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 will respect to the custom name by handling the widget.

If the view component name and the folder name of the view component don't match, you may need to manually write the view path as done in this example.

Display Name

You can also define a human-readable, localizable display name for the widget. This display name then can be used on the UI when needed. Display name is optional and can be defined using properties of the Widget attribute:

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", //Localization key
        DisplayNameResource = typeof(DashboardDemoResource) //localization resource
        )]
    public class MySimpleWidgetViewComponent : AbpViewComponent
    {
        public IViewComponentResult Invoke()
        {
            return View();
        }
    }
}

See the localization document to learn about localization resources and keys.

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 these issues when you properly relate the resources with the widget. You don't care about dependencies of the widget while using it.

Defining as Simple File Paths

The example widget below adds a style and a script file:

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 takes account these dependencies and properly adds to the view/page when you use the widget. Style/script files can be physical or virtual. It is completely integrated to the Virtual File System.

Defining Bundle Contributors

All resources for used widgets in a page are added as a bundle (bundled & minified in production if you don't configure otherwise). In addition to adding a simple file, you can take full power of the bundle contributors.

The sample code below does the same with the code above, but defines and uses bundle contributors:

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");
        }
    }
}

Bundle contribution system is very powerful. If your widget uses a JavaScript library to render a chart, then you can declare it as a dependency, so the JavaScript library is automatically added to the page if it wasn't added before. In this way, the page using your widget doesn't care about the dependencies.

See the bundling & minification documentation for more information about that system.

Authorization

Some widgets may need to be available only for authenticated or authorized users. In this case, use the following properties of the Widget attribute:

  • RequiresAuthentication (bool): Set to true to make this widget usable only for authentication users (user have logged in to the application).
  • RequiredPolicies (List<string>): A list of policy names to authorize the user. See the authorization document for more info about policies.

Example:

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();
        }
    }
}

Widget Options

As alternative to the Widget attribute, you can use the WidgetOptions to configure widgets:

Configure<WidgetOptions>(options =>
{
    options.Widgets.Add<MySimpleWidgetViewComponent>();
});

Write this into the ConfigureServices method of your module. All the configuration done with the Widget attribute is also possible with the WidgetOptions. Example configuration that adds a style for the widget:

Configure<WidgetOptions>(options =>
{
    options.Widgets
        .Add<MySimpleWidgetViewComponent>()
        .WithStyles("/Pages/Components/MySimpleWidget/Default.css");
});

Tip: WidgetOptions can also be used to get an existing widget and change its configuration. This is especially useful if you want to modify the configuration of a widget inside a module used by your application. Use options.Widgets.Find to get an existing WidgetDefinition.