Kirk Woll
Dec 08
Consuming Razor tag helpers from other tag helpers

Using a TagHelper from another TagHelper is not straightforward. This StackOverflow answer provides the basics for how to do it, but there's a ton of boilerplate.

For example, here's the answer to the original question:

var anchorTagHelper = new AnchorTagHelper
{
    Action = "Home",
};
var anchorOutput = new TagHelperOutput("a", new TagHelperAttributeList(), (useCachedResult, encoder) => new HtmlString());
var anchorContext = new TagHelperContext(
    new TagHelperAttributeList(new[] { new TagHelperAttribute("asp-action", new HtmlString("Home")) }),
    new Dictionary<object, object>(),
    Guid.NewGuid());
await anchorTagHelper.ProcessAsync(anchorContext, anchorOutput);
output.Content.SetHtmlContent(anchorOutput);

I've created a set of extension methods that reduces this friction. So instead of the above, you'd have:

var anchorTagHelper = new AnchorTagHelper
{
    Action = "Home",
};
var anchorOutput = await anchorTagHelper.GetOutput("a");
output.Content.SetHtmlContent(anchorOutput);

This makes nesting a TagHelper much more fun. Below is the extension method class:

public static class TagHelperExtensions
{
    public static Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, string tagName, string childContent)
    {
        return tagHelper.GetOutput(CreateContext(), tagName, childContent);
    }

    public static Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, string tagName, params IHtmlContent[] childContent)
    {
        return tagHelper.GetOutput(CreateContext(), tagName, childContent);
    }

    public static Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, string tagName)
    {
        return tagHelper.GetOutput(CreateContext(), tagName);
    }

    public static Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, string tagName, Func<TagHelperContext, bool, HtmlEncoder, TagHelperContent> getChildContent)
    {
        var context = CreateContext();
        return tagHelper.GetOutput(context, tagName, (useCachedResult, encoder) => getChildContent(context, useCachedResult, encoder));
    }

    public static Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, string tagName, Func<TagHelperContext, bool, HtmlEncoder, Task<TagHelperContent>> getChildContent)
    {
        var context = CreateContext();
        return tagHelper.GetOutput(context, tagName, (useCachedResult, encoder) => getChildContent(context, useCachedResult, encoder));
    }

    public static Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, TagHelperContext context, string tagName, string childContent)
    {
        var content = new DefaultTagHelperContent();
        content.Append(childContent);
        return tagHelper.GetOutput(context, tagName, (_, __) => content);
    }

    public static Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, TagHelperContext context, string tagName, params IHtmlContent[] childContent)
    {
        var content = new DefaultTagHelperContent();
        foreach (var item in childContent)
            content.AppendHtml(item);
        return tagHelper.GetOutput(context, tagName, (_, __) => content);
    }

    public static Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, TagHelperContext context, string tagName)
    {
        return tagHelper.GetOutput(context, tagName, (_, __) => new DefaultTagHelperContent());
    }

    public static Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, TagHelperContext context, string tagName, Func<bool, HtmlEncoder, TagHelperContent> getChildContent)
    {
        return tagHelper.GetOutput(context, tagName, (useCachedResult, encoder) => Task.FromResult(getChildContent(useCachedResult, encoder)));
    }

    public static async Task<TagHelperOutput> GetOutput(this TagHelper tagHelper, TagHelperContext context, string tagName, Func<bool, HtmlEncoder, Task<TagHelperContent>> getChildContent)
    {
        var textAreaOutput = new TagHelperOutput(tagName, new TagHelperAttributeList(), getChildContent);
        var textAreaContext = new TagHelperContext(new TagHelperAttributeList(), 
            context.Items, Guid.NewGuid().ToString());
        await tagHelper.ProcessAsync(textAreaContext, textAreaOutput);
        return textAreaOutput;
    }

    private static TagHelperContext CreateContext()
    {
        return new TagHelperContext(new TagHelperAttributeList(), new Dictionary<object, object>(), Guid.NewGuid().ToString());
    }
}

Note your comment will be put in a review queue before being published.