outline
The Very Basics of Nesting for Tag Helpers
In Asp.Net Core it is possible to create custom tag helpers that nests within other custom tag helpers. Consider the following example borrowed from another interesting post:
Here modal
, modal-body
and modal-footer
are custom tag helpers, the modal
tag helper encapsulates all the verbose html of the bootstrap modal. The modal-body
and the modal-footer
tag helpers allows the programmer to respectively specify the content of the modal’s body and footer.
modal-body
and modal-footer
are tag helpers created to be used only inside the modal
tag helper.
In this post we are going to see as usual a very basic example that will show how to create nested tag helpers.
I encourage you to check out Dave Paquette’s post that shows how to encapsulate a bootstrap modal in a tag helper and more, if you want to see more elaborated examples.
How nested tag helpers work?
We are going to create two tag helpers: ParentTagHelper
and ChildTagHelper
. Obviously ChildTagHelper
is going to be nested inside ParentTagHelper
. The following is the typical usage example:
<parent> <child></child> </parent>
Now let’s see how the two tag helpers are defined:
The ParentTagHelper.ProcessAsync
method performs the following steps:
- Create an instance of the
ParentChildContext
class which will act as a data transfer object between the parent and child tag helpers - Register the
ParentChildContext
instance in the context argument - Await the rendering of it’s children content
- When the children’s content is available, set the output content by using data in the
ParentChildContext
instance populated by theChildTagHelper
Notice here how the ParentTagHelper
creates the ParentChildContext
instance (parentChildContext
) and sets it to be passed to it’s nested tag helpers (child
in our case) by adding the instance to the context.Items
dictionary.
In fact, the context
argument in the TagHelper.Process
method is used to pass data between parent and child tag helpers; Items in the context.Items
dictionary are made available to the nested tag helper’s Process
method.
The ChildTagHelper
exhibits a slightly different Process
behavior compared to usual tag helpers; the content it generates is not passed to the output
argument, it is instead passed to the ParentChildContext
instance. Furthermore the output is forced to be suppressed with the output.SuppressOutput()
statement.
The parent tag helper relies on the previously described child behavior to get the content generated by the nested tag helpers. The parent is then responsible for assembling the final content that is going to be rendered in the razor view.
Finally the ParentTagHelper
class is slapped with the [RestrictChildren]
attribute that allows to specify the children tags that can be nested inside the parent tag helper.
Why defining a custom context?
In the previous example the ParentChildContext
class has been created so that its instances act as data transfer objects.
This is necessary as we cannot rely on context.Items
to pass data from child to parent. This inability is due to the fact that the TagHelperContext
instance (context
argument) passed to the child tag helper is actually a copy of the context
available in the parent tag helper.
This means that modifications on the context.Items
dictionary in the child tag helper does not affect the context.Items
in the parent tag helper.
On the flip side, objects referenced by the parent’s context.Items
dictionary are available in the child’s context.Items
, so we basically do as in ParentTagHelper.ProcessAsync
; we create a custom context, register in context.Items
and then it can be used by the parent tag helper and its children.