-
Notifications
You must be signed in to change notification settings - Fork 220
Tag Helpers: Redesign of Tag Helpers content mode #221
Description
This redesign unifies the various content modes of Tag Helpers and facilitates new features like conditional execution of Tag Helper contents, e.g. to support the proposed CacheTagHelper
aspnet/Mvc#1552.
- Get rid of the
ContentBehaviorAttribute
- Change members of
TagHelperOutput
:- Add
public string PreContent { get; set;}
: content to be rendered before child block orContent
- Add
public string PostContent { get; set;}
: content to be rendered after child block orContent
- Setting the
Content
property prevents the calling page from executing the child block and using its contents. - Add
public bool ContentSet { get; }
: indicates theContent
property has been explicitly set and the hosting view/page will not execute the child block and use the resulting value for the element content - Add
public Task<string> GetChildBlockContentAsync()
: executes the child block and returns the resulting string value- This is memoized such that only the first call actually executes the child block, subsequent calls simply return the cached result
- Add
public void SuppressOutput()
: sets theContent
andTagName
properties to null
- Add
This design assumes all child blocks of Tag Helpers are emitted in delegates to support delayed execution. We could potentially introduce a new attribute ModifyContent
that could be placed on Tag Helper classes to avoid the delegate creation except for the cases where the Tag Helper explicitly states it wants to modify content by presence of the attribute (by calling TagHelperOutput.GetChildBlockContentAsync
) if it's determined that the performance impact of always generating closure delegates is significant.
The GetChildBlockContentAsync
method should be protected from calls to Flush
and Write
in the child block by effectively delaying requested writes and flushes until after the Tag Helper has finished being written to the output. This will avoid inadvertent flushes to the output which would potentially reorder the content from what was intended.
Usage Examples
Prepending Content
Tag Helper class
public class TestTagHelper : TagHelper {
public void Process(TagHelperContext context, TagHelperOutput output) {
output.PreContent = "I will appear before the content<br />";
}
}
Razor file
<test>
Some content
</test>
HTML output
<test>
I will appear before the content<br />
Some content
</test>
Appending Content
Tag Helper class
public class TestTagHelper : TagHelper {
public void Process(TagHelperContext context, TagHelperOutput output) {
output.PostContent = "<br />I will appear after the content";
}
}
Razor file
<test>
Some content
</test>
HTML output
<test>
Some content
<br />I will appear after the content
</test>
Replacing Content
Tag Helper class
public class TestTagHelper : TagHelper {
public void Process(TagHelperContext context, TagHelperOutput output) {
output.Content = "I will replace the content";
}
}
Razor file
<test>
Some content run at @DateTime.Now
</test>
HTML output
<test>
I will replace the content
</test>
Modifying Content
Tag Helper class
public class TestTagHelper : TagHelper {
public async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) {
var childContent = await output.GetChildBlockContentAsync();
output.Content = childContent.ToUpperCase();
}
}
Razor file
<test>
Some content
</test>
HTML output
<test>
SOME CONTENT
</test>
Conditionally Suppressing Output
Tag Helper class
public class TestTagHelper : TagHelper {
public bool Render { get; set; }
public Task ProcessAsync(TagHelperContext context, TagHelperOutput output) {
if (!Render) {
output.SuppressContent();
}
}
}
Razor file
<p>Hello</p>
<test render="false">
Some content
</test>
<p>World</p>
HTML output
<p>Hello</p>
<p>World</p>