I have just noticed that if I have 2 tag helpers targeting the same element, both can be executed. The order in which they are executed depends on the order in which they are registered in _ViewImports.cshtml.
For example, I can create another tag helper for the anchor element:
[HtmlTargetElement("a", Attributes = "foo")]
public class FooTagHelper : TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
//Get the url from href attribute generated in the default AnchorTagHelper
var url = output.Attributes["href"].Value.ToString();
...
}
}
Use it as follows (notice I am also adding attributes of the default anchor helper like asp-controller
):
<a class="menu" asp-controller="Home" asp-action="Index" foo>Foo</a>
If this helper is registered in _ViewImports.cshtml after the default ASP ones:
Process
is called, the TagHelperOutput
already contains the href generated by the default AnchorTagHelper
. I could also update the anchor generated by the default tag helper in any way I like.Is there any degree of control over this behavior?
You might want to decide whether or not to execute further helpers targeting the same element (As if sealing your output). You might also want to allow other helpers, but make sure some attribute wasn't modified.
Override the readonly property order, like this:
[HtmlTargetElement("a", Attributes = "foo")]
public class FooTagHelper : TagHelper
{
// This should be the last tag helper on any tag to run
public override int Order => int.MaxValue;
public override async Task ProcessAsync(TagHelperContext context,
TagHelperOutput output)
{
//...
}
}
Reading the source code of the TagHelperRunner
class, I realized that the same TagHelperContext
and TagHelperOutput
will be shared for all the tag helpers found for the same element, which will be processed orderd by the ITagHelper.Order
property.
So you can control the order in which they are executed by assigning appropriated values to the Order property. As a reference, this is the TagHaelperRunner.RunAsync
method:
public async Task<TagHelperOutput> RunAsync([NotNull] TagHelperExecutionContext executionContext)
{
var tagHelperContext = new TagHelperContext(
executionContext.AllAttributes,
executionContext.Items,
executionContext.UniqueId,
executionContext.GetChildContentAsync);
var tagHelperOutput = new TagHelperOutput(
executionContext.TagName,
executionContext.HTMLAttributes)
{
SelfClosing = executionContext.SelfClosing,
};
var orderedTagHelpers = executionContext.TagHelpers.OrderBy(tagHelper => tagHelper.Order);
foreach (var tagHelper in orderedTagHelpers)
{
await tagHelper.ProcessAsync(tagHelperContext, tagHelperOutput);
}
return tagHelperOutput;
}
The default Order if you extend the class TagHelper
is 0.
The MVC tag helpers like AnchorTagHelper
or InputTagHelper
seem to have the order defined as -1000.
So far, I have also found that you can query some of the properties in TagHelperOutput
to check if a previous tag helper has modified the output. Although you cannot know if a tag helper with higher order (executed after yours) modifies the output:
TagHelperOutput.IsContentModified
will return true only when the Content is modified (Not when the attributes or the PreElement
, PreContent
, PostElement
, PostContent
are modified)
TagHelperOutput.PreElement.IsModified
and similar for PreContent
, PostElement
and PostContent
will return true when those have been modified.
Content set by a previous tag helper could be removed by calling TagHelperOutput.Content.Clear()
and similar for Pre/Post Element/Context properties.
Content can be completely suppressed by calling TagHelperOutput.SuppressOutput()
which calls clear on every of those properties and set TagName as null. If you want the tag helper to render something you will then need to assign them again.
Finally, if you had to share some data between multiple tag helpers for the same element, you can use the TagHelperContext.Items
dictionary.
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