Creating a Widget provider

For a more in-depth view on widgets, zones and invokers, please refer to Widgets.

There are many ways to display or inject content in Smartstore. One of the methods is to use a Widget. Following the tutorial Adding tabs tutorial, you'll be adding a widget to your project.

Implementing the IActivatableWidget

Using the module from the Adding tabs tutorial, we'll start with the Module.cs file. You'll need to add the interface IActivatableWidget to the implementation.

public class Module : ModuleBase, IConfigurable, IActivatableWidget

This will force you to implement the following two Methods:

public Widget GetDisplayWidget(string widgetZone, object model, int storeId)

public string[] GetWidgetZones()

GetWidgetZones is a string array containing every widget zone we want to access.

public string[] GetWidgetZones()
{
    return new string[] { "target_widget_zone_name" };
}

In this tutorial we'll be using the productdetails_pictures_top widget zone. It is placed above the product picture when using the Frontend.

More examples of widget zone names can be found under List of all core widget zone names.

If you want to see the widget zones in your store, you can use the DevTools plugin.

  1. Install the Smartstore Developer Tools plugin.

  2. Click on Configure.

  3. Activate the option to Display Widget Zones.

A simple implementation for GetDisplayWidget would be

public Widget GetDisplayWidget(string widgetZone, object model, int storeId)
    => new ComponentWidget(typeof(HelloWorldViewComponent), new {widgetZone, model, storeId});

which creates a ComponentWidget for all widget zones we specified in GetWidgetZones. Your code should look something like this:

Module.cs
public class Module : ModuleBase, IConfigurable, IActivatableWidget
{
    // ...

    public Widget GetDisplayWidget(string widgetZone, object model, int storeId)
        => new ComponentWidget(typeof(HelloWorldViewComponent), new {widgetZone, model, storeId});

    public string[] GetWidgetZones()
        => new string[] { "productdetails_pictures_top" };

    // ...
}

Create the ViewComponentModel

For the widget to access MyTabValue from the previous tutorial, you'll need to create a new model.

  1. Right click on the Models folder in the Solution Explorer.

  2. Place a new class called ViewComponentModel.cs in this folder.

Then add the following lines:

ViewComponentModel.cs
using Smartstore.Web.Modelling;

namespace MyOrg.HelloWorld.Models
{
    public class ViewComponentModel : ModelBase
    {
        public string MyTabValue { get; set; }
    }
}

Adding the ViewComponent

  1. Right click on the project in the Solution Explorer.

  2. Add a new folder. According to our guidelines we call it Components.

  3. Place a new class called HelloWorldViewComponent.cs in this folder.

This class implements SmartViewComponent.

public class HelloWorldViewComponent : SmartViewComponent

By implementing SmartViewComponent we have access to a Logger, Localization and other common utilities and services.

To use the database context, add it to the constructor using dependency injection.

private readonly SmartDbContext _db;

public HelloWorldViewComponent(SmartDbContext db)
{
    _db = db;
}

Next you'll add the InvokeAsync method. It is called each time the widget zones specified in GetWidgetZones is about to get rendered. The model is passed from GetDisplayWidget.

public async Task<IViewComponentResult> InvokeAsync(string widgetZone, object model)

In case you're handling multiple widget zones and need to differentiate between them, you might add an if- or a switch-block for widgetZone. If you just want to make sure that your widget isn't displaying anything, when it is not supposed to, add the following lines:

if (widgetZone != "productdetails_pictures_top")
{
    return Empty();
}

After checking whether the correct model is being used, you fetch the product from the database and get the specified MyTabValue.

if (model.GetType() != typeof(ProductDetailsModel))
{
    return Empty();
}

var productModel = (ProductDetailsModel)model;
var product = await _db.Products.FindByIdAsync(productModel.Id);
var attributeValue = product.GenericAttributes.Get<string>("HelloWorldMyTabValue");

And finally you create the ViewComponentModel and return the View.

var viewComponentModel = new ViewComponentModel
{
    MyTabValue = attributeValue
};

return View(viewComponentModel);

For you to use ProductDetailsModel, add these lines to you project file:

<ItemGroup>
    <ProjectReference Include="..\..\Smartstore.Web\Smartstore.Web.csproj">
        <Private>False</Private>
        <CopyLocal>False</CopyLocal>
        <CopyLocalSatelliteAssemblies>False</CopyLocalSatelliteAssemblies>
    </ProjectReference>
</ItemGroup>

To get to the project file, simply click on your project in the Solution Explorer.

The final code looks like this:

HelloWorldViewComponents.cs
public class HelloWorldViewComponent : SmartViewComponent
{
    private readonly SmartDbContext _db;

    public HelloWorldViewComponent(SmartDbContext db)
    {
        _db = db;
    }

    public async Task<IViewComponentResult> InvokeAsync(string widgetZone, object model)
    {
        if (widgetZone != "productdetails_pictures_top")
        {
            return Empty();
        }

        if (model.GetType() != typeof(ProductDetailsModel))
        {
            return Empty();
        }

        var productModel = (ProductDetailsModel)model;
        var product = await _db.Products.FindByIdAsync(productModel.Id);
        var attributeValue = product.GenericAttributes.Get<string>("HelloWorldMyTabValue");

        var viewComponentModel = new ViewComponentModel
        {
            MyTabValue = attributeValue
        };

        return View(viewComponentModel);
    }
}

Adding the cacheable routes

You must add this code to your module if your ViewComponent should be cacheable. For more information see output cache.

  1. Right click on the project in the Solution Explorer.

  2. Place a new class called CacheableRoutes.cs in this folder.

Then add these lines:

CacheableRoutes.cs
using Smartstore.Core.OutputCache;

namespace MyOrg.HelloWorld
{
    internal sealed class CacheableRoutes : ICacheableRouteProvider
    {
        public int Order => 0;

        public IEnumerable<string> GetCacheableRoutes()
        {
            return new string[]
            {
                "vc:MyOrg.HelloWorld/HelloWorld"
            };
        }
    }
}

Adding the View

  1. Right click on the Views folder of your module in the Solution Explorer.

  2. Add a new folder. According to the guidelines we call it Shared/Components/HelloWorld.

  3. Place a new Razor View called Default.cshtml in this folder.

Add the following lines for a simple output:

Default.cshtml
@model ViewComponentModel

@{
    Layout = "";
}

<span>Widget content: @Model.MyTabValue</span>

Conclusion

Now you should be able to define a property in your product catalog and see it displayed above the product picture.

In this tutorial you built a widget using the IActivatableWidget interface and specified your widget zones. You created a ViewComponent and bound it to different widget zones. Hopefully this will get you started with widgets and enable you to build more complex modules.

The code for this tutorial can be found in the examples repository.

Last updated

Was this helpful?