In a recent asp.net core project, I needed to create a somewhat complex list editor widget and I decided to encapsulate it in a tag helper.
Since it would be painful to build the widget's content using interpolated strings under the tag helper's
Process method, I started searching for a way to render a cshtml template similar to what
@Html.Partial providers but usable inside the
After doing some googling in vain, I posted an issue on the asp.net core Mvc project to suggest adding a mechanism for rendering cshtml templates from tag helpers. frankabbruzzese pointed out then in the discussion that it was possible to get a working
IHtmlHelper inside a custom tag helper and to use its
In this post we are going to see how to:
- get and properly initialize an
Htmlhelperin a custom tag helper
- pass data to the cshtml template
As usual the full project code is available on Github. But first off let's discuss why you should not do this.
Razor view components
When I was building the list editor widget I was not aware of the new Razor View Component mechanism , which basically allows to render a partial template, to pass it data and to do some processing before the actual rendering.
It seems that using View Components is a cleaner approach as we are going to see in an upcoming post.
But still one might need to use an
HtmlHelper inside a tag helper maybe for some reason I am not anticipating. That's why I want to share what I learned.
Getting an IHtmlHelper through Dependency Injection and properly initializing it
By using the dependency injection mechanism in asp.net core it is possible to get an
IHtmlHelper instance, the only catch is that the provided instance is not ready for use; it needs to be contextualized i.e. to be set with the
ViewContext object of the view in which the tag helper is rendered.
This is achieved by the
(htmlHelper as IViewContextAware).Contextualize(ViewContext); statement which needs the current
In the previous example the TagHelper constructor will be used by the dependency injection context to provide an
IHtmlHelper instance and the
ViewContext property slapped with the
[ViewContext] attribute will be populated by the current ViewContext. Notice how the html helper is contextualized at the start of the
HtmlHelper is then used to render a cshtml view (
~/Views/Shared/Template.cshtml in the example), by calling the
Passing data to the view
Passing data to the razor view is similar to passing data to the
Controller.View method, you have basically 3 ways:
- passing a model object to a strongly typed view
- populating the
- populating the
Under the tag helper
ProcessAsync method it is possible to use the
ViewBag properties contained in the
Notice how the model as been passed explicitly and how the
ViewData and its expando object facade the
ViewBag are passed implicitly. The data is then accessed in the rendered razor partial template as in the following example:
Creating a new ViewData dictionary instance
One last thing: While creating the list editor widget, I had to actually create a new instance of the
ViewData dictionary instead of using the one in the
HtmlHelper instance. I will skip the details of the issue I encountered in the interest of brievety.
In asp.net core the most convenient constructor that allows to create an instance "from scratch" in the
ViewDataDictionary class has the following signature:
ViewDataDictionary(IModelMetadataProvider metadataProvider, ModelStateDictionary modelState).
IModelMetadataProvider instance can be requested through dependency injection and the
ModelStateDictionary can be instantiated by using its parameterless constrcutor.