Skip to content
This repository was archived by the owner on Dec 19, 2018. It is now read-only.
This repository was archived by the owner on Dec 19, 2018. It is now read-only.

Tag Helpers: Redesign of Tag Helpers content mode #221

@DamianEdwards

Description

@DamianEdwards

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.

  1. Get rid of the ContentBehaviorAttribute
  2. Change members of TagHelperOutput:
    1. Add public string PreContent { get; set;}: content to be rendered before child block or Content
    2. Add public string PostContent { get; set;}: content to be rendered after child block or Content
    3. Setting the Content property prevents the calling page from executing the child block and using its contents.
    4. Add public bool ContentSet { get; }: indicates the Content 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
    5. 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
    6. Add public void SuppressOutput(): sets the Content and TagName properties to null

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>

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions