在 ASP.NET 核心 MVC 中嵌套 TagHelper
Nesting TagHelpers in ASP.NET Core MVC
ASP.NET核心TagHelper documentation给出了以下例子:
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;
}
}
然后可以在您的 Razor .cshtml 中使用它,如下所示:
<website-information info="new WebsiteContext {
Version = new Version(1, 3),
CopyrightYear = 1790,
Approved = true,
TagsToShow = 131 }"/>
这将生成以下 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>
这是非常丑陋的标签助手语法。有没有办法嵌套另一个标签助手并获得完整的智能感知,以便网站信息的唯一允许子项可以是上下文?请参见下面的示例:
<website-information>
<context version="1.3" copyright="1790" approved tags-to-show="131"/>
</website-information>
在我的用例中,website-information 元素已经有很多属性,我想添加一个 或更多 个单独的嵌套元素。
更新
我已在 ASP.NET GitHub 页面上提出 this 建议以实现 TagHelpers 的此功能。
您当然可以嵌套标签助手,尽管视图组件、部分视图或显示模板等其他选项可能更适合 OP 描述的场景。
这可能是一个非常简单的 child 标签助手:
[HtmlTargetElement("child-tag", ParentTag="parent-tag")]
public class ChildTagHelper : TagHelper
{
public string Message { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
// Create parent div
output.TagName = "span";
output.Content.SetContent(Message);
output.TagMode = TagMode.StartTagAndEndTag;
}
}
这也可以是另一个简单的 parent 标签助手:
[HtmlTargetElement("parent-tag")]
[RestrictChildren("child-tag")]
public class ParentTagHelper: TagHelper
{
public string Title { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "div";
// Add some specific parent helper html
var header = new TagBuilder("h1");
header.Attributes.Add("class", "parent-title");
header.InnerHtml.Append(this.Title);
output.PreContent.SetContent(header);
// Set the inner contents of this helper(Will process any nested tag helpers or any other piece of razor code)
output.Content.SetContent(await output.GetChildContentAsync());
}
}
在剃须刀视图中,您可以编写以下内容:
<parent-tag title="My Title">
<child-tag message="This is the nested tag helper" />
</parent-tag>
将呈现为:
<div>
<h1 class="parent-title">My Title</h1>
<span>This is the nested tag helper</span>
</div>
您可以选择性地强制以特定方式嵌套标签助手:
- 在父标签助手中使用
[RestrictChildren("child-tag", "another-tag")]
来限制允许的嵌套标签助手
- 在声明子标签助手时使用
ParentTag
参数强制将标签嵌套在特定的父标签中 [HtmlTargetElement("child-tag", ParentTag = "parent-tag")]
我没有在网上找到多重嵌套标签助手的好例子;所以,我在 MultiplyNestedTagHelpers GitHub Repository.
创建了一个
当使用多级嵌套标签助手时,为每个可以包含子标签的标签设置一个 "context" class 很重要。这些上下文 classes 被子标签用来写入它们的输出。上下文 class 是一个常规的 POCO class,每个子标签都有一个 属性。属性可能是字符串,但我使用 StringBuilder。例如,
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.
在每个父标签的 Process 方法中,您需要实例化父标签的关联上下文 class 并将这个新对象添加到 TagHelperContext 对象的 Items 集合中。例如:
//create context for this tag helper
var tableContext = new MyTableContext();
context.Items.Add(typeof(MyTableContext), tableContext);
在子标记的 Process 方法中,您像这样写入父标记的已注册上下文对象:
//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();
返回父标签的 Process 方法,您会收到子标签的输出,如下所示:
//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());
ASP.NET核心TagHelper documentation给出了以下例子:
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;
}
}
然后可以在您的 Razor .cshtml 中使用它,如下所示:
<website-information info="new WebsiteContext {
Version = new Version(1, 3),
CopyrightYear = 1790,
Approved = true,
TagsToShow = 131 }"/>
这将生成以下 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>
这是非常丑陋的标签助手语法。有没有办法嵌套另一个标签助手并获得完整的智能感知,以便网站信息的唯一允许子项可以是上下文?请参见下面的示例:
<website-information>
<context version="1.3" copyright="1790" approved tags-to-show="131"/>
</website-information>
在我的用例中,website-information 元素已经有很多属性,我想添加一个 或更多 个单独的嵌套元素。
更新
我已在 ASP.NET GitHub 页面上提出 this 建议以实现 TagHelpers 的此功能。
您当然可以嵌套标签助手,尽管视图组件、部分视图或显示模板等其他选项可能更适合 OP 描述的场景。
这可能是一个非常简单的 child 标签助手:
[HtmlTargetElement("child-tag", ParentTag="parent-tag")]
public class ChildTagHelper : TagHelper
{
public string Message { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
// Create parent div
output.TagName = "span";
output.Content.SetContent(Message);
output.TagMode = TagMode.StartTagAndEndTag;
}
}
这也可以是另一个简单的 parent 标签助手:
[HtmlTargetElement("parent-tag")]
[RestrictChildren("child-tag")]
public class ParentTagHelper: TagHelper
{
public string Title { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "div";
// Add some specific parent helper html
var header = new TagBuilder("h1");
header.Attributes.Add("class", "parent-title");
header.InnerHtml.Append(this.Title);
output.PreContent.SetContent(header);
// Set the inner contents of this helper(Will process any nested tag helpers or any other piece of razor code)
output.Content.SetContent(await output.GetChildContentAsync());
}
}
在剃须刀视图中,您可以编写以下内容:
<parent-tag title="My Title">
<child-tag message="This is the nested tag helper" />
</parent-tag>
将呈现为:
<div>
<h1 class="parent-title">My Title</h1>
<span>This is the nested tag helper</span>
</div>
您可以选择性地强制以特定方式嵌套标签助手:
- 在父标签助手中使用
[RestrictChildren("child-tag", "another-tag")]
来限制允许的嵌套标签助手 - 在声明子标签助手时使用
ParentTag
参数强制将标签嵌套在特定的父标签中[HtmlTargetElement("child-tag", ParentTag = "parent-tag")]
我没有在网上找到多重嵌套标签助手的好例子;所以,我在 MultiplyNestedTagHelpers GitHub Repository.
创建了一个当使用多级嵌套标签助手时,为每个可以包含子标签的标签设置一个 "context" class 很重要。这些上下文 classes 被子标签用来写入它们的输出。上下文 class 是一个常规的 POCO class,每个子标签都有一个 属性。属性可能是字符串,但我使用 StringBuilder。例如,
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.
在每个父标签的 Process 方法中,您需要实例化父标签的关联上下文 class 并将这个新对象添加到 TagHelperContext 对象的 Items 集合中。例如:
//create context for this tag helper
var tableContext = new MyTableContext();
context.Items.Add(typeof(MyTableContext), tableContext);
在子标记的 Process 方法中,您像这样写入父标记的已注册上下文对象:
//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();
返回父标签的 Process 方法,您会收到子标签的输出,如下所示:
//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());