ViewComponents

Specialized ViewComponents

Suppose you want to introduce an inheritance chain in ASP.Net Core to use some businesslogic in a baseclass :

namespace Foo.Core.Web
{
	using Microsoft.AspNetCore.Mvc;
	using System.Collections.Generic;

	public class CoolBaseViewComponentDetail : ViewComponent
	{
		protected List<string> getItems(int itemCount)
		{
			List<string> items = new List<string>();
			for (int i = 0; i < itemCount; i++)
			{
			  items.Add(string.Format("string {0}", i));
			}

			return (items);
		}
	}
 
	public class SpecializedViewComponentDetail : CoolBaseViewComponentDetail
	{
		public virtual IViewComponentResult Invoke(int numberOfItems)
		{
			List<string> items = getItems(numberOfItems);
			return (View(viewName: "~/Src/ViewComponentSpecialized.Detail.cshtml", model: items));
		}
	}
}

ViewComponentSpecialized.Detail renders like this :

@model System.Collections.Generic.List<string>
<ul>
    @foreach( string item in Model)
    {
        <li>@item</li>
    }
</ul>

If you invoke this viewcomponent you get an exception :

@await Component.InvokeAsync( name:"SpecializedViewComponentDetail", arguments:new { numberOfItems = 3 } )

InvalidOperationException: Could not find an 'Invoke' or 'InvokeAsync' method for the view component 'Foo.Core.Web.CoolBaseViewComponentDetail'.

ASP.Net core requires an ‘Invoke’-method on every ViewComponent-specialized class, even if it is not directly called. Solution is to introduce this method but to let it throw an exception when directly called:

namespace Foo.Core.Web
{
	using Microsoft.AspNetCore.Mvc;
	using System.Collections.Generic;
		
	public class CoolBaseViewComponentDetail : ViewComponent
	{
		public virtual IViewComponentResult Invoke(int numberOfItems)
		{
			throw new System.NotSupportedException("Specialized classes should implement this");
		}

		protected List<string> getItems(int itemCount)
		{
			List<string> items = new List<string>();
			for (int i = 0; i < itemCount; i++)
			{
			  items.Add(string.Format("string {0}", i));
			}

			return (items);
		}
	}
 
	public class SpecializedViewComponentDetail : CoolBaseViewComponentDetail
	{
		public virtual IViewComponentResult Invoke(int numberOfItems)
		{
			List<string> items = getItems(numberOfItems);
			return (View(viewName: "~/Src/ViewComponentSpecialized.Detail.cshtml", model: items));
		}
	}
}

Now the view renders:

o string 0
o string 1
o string 2

cheers, Stephan