The ASP.NET Core TagHelper documentation gives the following example:
public class WebsiteContext
{
public Version Version { get; set; }
public int CopyrightYear { get; set; }
public bool Approved { get; set; }
public int TagsToShow { get; set; }
}
[TargetElement("website-information")]
public class WebsiteInformationTagHelper : TagHelper
{
public WebsiteContext Info { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "section";
output.Content.SetContent(
$@"<ul><li><strong>Version:</strong> {Info.Version}</li>
<li><strong>Copyright Year:</strong> {Info.CopyrightYear}</li>
<li><strong>Approved:</strong> {Info.Approved}</li>
<li><strong>Number of tags to show:</strong> {Info.TagsToShow}</li></ul>");
output.TagMode = TagMode.StartTagAndEndTag;
}
}
This can then be used in your Razor .cshtml as follows:
<website-information info="new WebsiteContext {
Version = new Version(1, 3),
CopyrightYear = 1790,
Approved = true,
TagsToShow = 131 }"/>
This will generate the following HTML:
<section>
<ul>
<li><strong>Version:</strong> 1.3</li>
<li><strong>Copyright Year:</strong> 1790</li>
<li><strong>Approved:</strong> true</li>
<li><strong>Number of tags to show:</strong> 131 </li>
</ul>
</section>
This is pretty ugly tag helper syntax. Is there some way to nest another tag helper and get full intelli-sense so that the only allowed child of website-information can be context? See example below:
<website-information>
<context version="1.3" copyright="1790" approved tags-to-show="131"/>
</website-information>
In my use case, the website-information element already has many attributes and I want to add one or more separate nested elements.
UPDATE
I have raised this suggestion on the ASP.NET GitHub page to implement this feature for TagHelpers.
I did not find a good example of multiply nested tag helpers on the web; so, I created one at MultiplyNestedTagHelpers GitHub Repository.
When working with more than one level of nested tag helper, it is important to setup a "context" class for each tag that can contain child tags. These context classes are used by the child tags to write their output. The context class is a regular, POCO class that has a property for each child tag. The properties may be strings, but I use StringBuilder. For example,
public class MyTableContext{
public StringBuilder TableHeaderBuilder { get; set; } = new StringBuilder();
public StringBuilder TableBodyBuilder { get; set; } = new StringBuilder();
}
public class MyTableHeaderContext {
public StringBuilder RowBuilder { get; set; } = new StringBuilder();
}
//...etc.
In each parent tag's Process method, you need to instantiate the parent's associated context class and add this new object to the TagHelperContext object's Items collection. For example:
//create context for this tag helper
var tableContext = new MyTableContext();
context.Items.Add(typeof(MyTableContext), tableContext);
In the child tag's Process method, you write to the parent's registered context object like this:
//get reference to parent context, and append content to the relevant builder
var tableContext = context.Items[typeof(MyTableContext)] as MyTableContext;
tableContext.TableHeaderBuilder.Append(sb.ToString());
//suppress output (for any tag with a parent tag)
output.SuppressOutput();
Back in the parent tag's Process method, you receive the child tag's output like this:
//you can use a StringBuilder to build output
// or just write to output.Content.AppendHtml() for top-level tags
var sb = new StringBuilder();
//...
//retrieve the child output and append it to sb
await output.GetChildContentAsync();
sb.Append(tableHeaderContext.RowBuilder.ToString());
//...
//write to TagHelperOutput for top-level tags
output.Content.AppendHtml(sb.ToString());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With