Widgets
In this tutorial we will add a widget to display all the products. We will then add some parameter to limit the number to display.
Let's start by creating the following "Migration14.cs" file:
using FluentMigrator;
namespace DemoShop.Shop.Migrations;
[Migration(201908141856)]
public class Migration14 : AutoReversingMigration {
public override void Up() {
Insert.IntoTable("WidgetTypes").Row(new { Name = "Products", ComponentName = "DemoShop.Shop.ViewComponents.Products", AllowContentWidgets = false });
}
}
Next we'll add a View Component for our widgets. Add the following file called "ProductsViewComponent.cs" to a new folder called "ViewComponents":
using System.Linq;
using System.Threading.Tasks;
using DemoShop.Shop.Services;
using DemoShop.Shop.ViewModels.Shared.Components;
using Microsoft.AspNetCore.Mvc;
namespace DemoShop.Shop.ViewComponents;
[ViewComponent(Name = "DemoShop.Shop.ViewComponents.Products")]
public class ProductsViewComponent : ViewComponent {
private readonly IShopService _shopService;
public ProductsViewComponent(IShopService shopService) {
_shopService = shopService;
}
public async Task<IViewComponentResult> InvokeAsync() {
return View((await _shopService.GetProductsAsync()).Select(p => new ProductViewModel(p)).ToList());
}
}
Next we'll add our view model.
- Add a folder called “Shared” to the “ViewModels” folder.
- Within that folder create a sub-folder called “Components”.
- Now the following file called "ProductViewModel.cs" within this folder:
using DemoShop.Shop.Models;
namespace DemoShop.Shop.ViewModels.Shared.Components;
public class ProductViewModel {
public ProductViewModel(Product product) {
Id = product.Id;
Name = product.Name;
}
public int Id { get; }
public string Name { get; }
}
Finally we'll add the view.
- First add a folder called "Components" in the "Views/Shared" folder.
- Within that folder create a sub-folder called “Products”.
- Now add the following file called "Default.cshtml" within this folder:
@model IList<DemoShop.Shop.ViewModels.Shared.Components.ProductViewModel>
@if (Model.Count > 0) {
<ul>
@foreach (var product in Model) {
<li><a asp-action="Details" asp-route-id="@product.Id">@Format(product.Name)</a></li>
}
</ul>
}
You can go ahead and add your widget.
Whilst this works fine, often you'll need to customise the widget by some parameters. We'll extend the widget above and a way to limit the number of products to display.
First we'll update the widget type we added above to specify the parameters type. Create the following "Migration15.cs" file:
using System;
using FluentMigrator;
namespace DemoShop.Shop.Migrations;
[Migration(201908141858)]
public class Migration15 : Migration {
public override void Up() {
Update.Table("WidgetTypes").Set(new { ParametersTypeAssembly = "DemoShop.Shop", ParametersTypeClass = "DemoShop.Shop.Models.ProductsParameters", ParametersTypeAction = "Products", ParametersTypeController = "WidgetParameters", ParametersTypeArea = "DemoShop.Shop" }).Where(new { ComponentName = "DemoShop.Shop.ViewComponents.Products" });
}
public override void Down() {
throw new NotImplementedException();
}
}
Now we'll add the following model called "ProductsParameters.cs" to the "Models" folder:
using System.ComponentModel.DataAnnotations;
namespace DemoShop.Shop.Models;
public class ProductsParameters {
[Required]
public int NumProducts { get; set; } = 5;
}
Next let's add the following “WidgetParametersController.cs” file to the “Controllers” folder:
using DemoShop.Shop.Models;
using Kit.Mvc.Filters;
using Kit.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc;
namespace DemoShop.Shop.Controllers;
[Area("DemoShop.Shop"), Route("parameters/widgets"), HtmlFieldPrefix("Parameters")]
public class WidgetParametersController : Controller {
[AjaxOrChildActionOnly, HttpGet("products")]
public IActionResult Products([BindRouteData] ProductsParameters parameters) {
return PartialView(parameters);
}
}
Next we'll add a view for the “Products” action. Create a file called "Products.cshtml" in a new "Views/WidgetParameters" folder with the following content:
@model DemoShop.Shop.Models.ProductsParameters
<div class="card shadow">
<div class="card-header"><h2>@Text["Parameters"]</h2></div>
<div class="card-body">
<div class="form-group">
<label asp-for="NumProducts" class="form-label"></label>
<editor for="NumProducts" />
</div>
</div>
</div>
Finally let's update our view component to make use of our parameters. Open the “ViewComponents/ProductsViewComponent.cs” file and replace the “InvokeAsync” action method with the following (adding any missing namespaces):
public async Task<IViewComponentResult> InvokeAsync(ProductsParameters parameters) {
return View((await _shopService.GetProductsAsync(1, parameters.NumProducts)).Select(p => new ProductViewModel(p)).ToList());
}
Now you can go ahead and update your widget to limit the number of products to display.