outline
Revisiting Programmatic Tag Helper Rendering
In the Rendering a tag helper inside another tag helper post, I showed how it was possible to instantiate a tag helper, to run its Process
method and to get the generated result as a string.
Recently, after doing some investigation about the tag helper rendering process I had some insights about how to create TagHelperOutput
and TagHelperContext
objects as well as how to leverage existing behaviors to get the generated content as a string.
We are going to bring back the renderInnerTagHelper
method from the example of the Rendering a tag helper inside another tag helper post:
Now let’s review this chunk of code step by step.
Creating the TagHelperOutput
The TagHelperOutput
object gets populated by the generated content inside the tag helper’s Process
method.
We need to supply some arguments to the constructor of the TagHelperOutput
class to create one:
- The TagName of the tag being rendered
- The list of html attributes related to the tag being rendered
- A callback that asynchronously returns any eventual child content contained inside the tag being rendered
The razor template engine seems to provide the TagName
value for ‘standard’ html tag helpers such as the form
and img
tag helpers. So it is the responsibility of the creator of the TagHelperOutput
to provide this value. Nevertheless it can be safe to ignore this argument for custom tag helpers that sets their TagName
by them selves.
The attribute list argument is straightforward; just supply the list of desired html attributes.
The callback argument is generated by the razor template engine when rendering nested tag helpers. It should contain code that generates child content and that runs any nested tag helpers.
We can safely assume that it is safe to provide an async function that returns empty TagHelperContent
when the tag helper does not contain child content.
Finally, the razor template engine also sets the TagMode
property on the TagHelperOutput
object. It is wise to do so when creating TagHelperOutput
s manually.
Creating the TagHelperContext
The responsibility of the TagHelperContext
is to contain data that is going to be passed from parent to child tag helpers when they are Processed.
In the previous example, we just used the context object provided by the razor template engine. But let’s see how to create one still.
The constructor for TagHelperContext
takes the following arguments:
- The same attribute list used for creating the
TagHelperOutput
- An Items
Dictionary<object, object>
that contains the data passed along with the context - A unique identifier for the rendering scope
The first two arguments are straightforward.
The last argument is used internally by the ASP.NET Core framework for some caching mechanism and is provided by the razor template engine, furthermore the value generated of the unique Id generated by razor seems to be a Guid striped of its hyphens(726e2629c3b34a6b92dd8436f55ad968 for example). So I think it is OK to provide a bogus value since we are not soliciting the caching behavior when rendering tag helpers programmatically.
Finally, I would like to point out that the TagHelperContext
instance might not be used if there are no nested content involved.
Getting the generated content as a string
In the renderInnerTagHelper
method, the tag helper output
is manually converted into a string and an additional renderHtmlAttributes
method is defined to generate the html attributes.
Actually, the TagHelperOutput
class implements the IHtmlContent
interface and provides an implementation for the WriteTo
method that writes the output content into a supplied TextWriter
.
TagHelperOutput.WriteTo
takes two arguments:
- A
TextWriter
object, here we just pass a newStringWriter
- An
HtmlEncoder
object, that we can get by dependency injection
All we have to do after calling WriteTo
is to read the results from the supplied TextWriter
.
Clearly, this is the way to go to get the rendered content.
Revisiting the previous example
The following is the updated renderInnerTagHelper
method, you can see that we took into account the previous guidelines:
The full code is, of course, available in the RenderingTagHelperInsideAnother Github repository.