【问题标题】:Nesting TagHelpers in ASP.NET Core MVC在 ASP.NET Core MVC 中嵌套 TagHelper
【发布时间】:2015-09-21 10:25:43
【问题描述】:

ASP.NET Core 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>

在我的用例中,网站信息元素已经有很多属性,我想添加一个或多个单独的嵌套元素。

更新

我在 ASP.NET GitHub 页面上提出了this 建议,为 TagHelpers 实现此功能。

【问题讨论】:

  • 为什么不将单独的参数添加到 website-information 标签助手而不是单个 info 参数?你可以嵌套标签助手,但你不能强制只有 &lt;context&gt; 助手嵌套在 &lt;website-information&gt; 助手中
  • @DanielJ.G. 这样做有很多理由。 1. website-information 上已经有很多属性 2. 如果上下文作为子元素更符合逻辑 3. 如果 context 属性在逻辑上分组在一起 4. 你可以有多个 context 元素。

标签: asp.net razor asp.net-core asp.net-core-mvc tag-helpers


【解决方案1】:

您当然可以嵌套标签助手,尽管视图组件、部分视图或显示模板等其他选项可能更适合 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")]

【讨论】:

  • ParentTag 参数内容如下:“所需的直接父级的 HTML 元素名称。空值表示允许任何 HTML 元素名称。”我可以改用 CSS 选择器吗?父标签只是一个&lt;select&gt;,但我不想影响&lt;select&gt; 的所有孩子,也许只是&lt;select class="special"&gt; 的孩子——我应该为父母制作自己的TagHelper,这样我就可以针对特定元素?或者我可以使用 Parent="select.special" 这样的 CSS 选择器吗?
  • 不是在谈论直接实现,但这将有助于在模型中实现 foreach 对吗?
  • 在 ParentTagHelper 中注意 NET 5 将 SetContent 替换为 SetHtmlContent
  • 有没有办法查到孩子的指数?假设我们有TabTagHelper 作为父母TabHeaderTagHelper 作为孩子,下面的代码:&lt;tab&gt;&lt;tab-header title='Tab 1' /&gt;&lt;tab-header title='Tab 2' /&gt;&lt;/tab&gt;,我想知道Tab 2是第二个孩子,在TabHeaderTagHelper 类中,这可能吗?
【解决方案2】:

我没有在网上找到一个很好的多嵌套标签助手的例子;所以,我在MultiplyNestedTagHelpers GitHub Repository 创建了一个。

当使用多于一层的嵌套标签助手时,为每个可以包含子标签的标签设置一个“上下文”类是很重要的。子标签使用这些上下文类来编写它们的输出。上下文类是一个常规的 POCO 类,每个子标签都有一个属性。属性可能是字符串,但我使用 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 方法中,需要实例化父标签关联的上下文类,并将这个新对象添加到 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());

【讨论】:

  • 是的,这似乎是目前唯一真正的解决方案。投上一票,欢迎来到 StackOverflow!
  • 谢谢,这正是我一直在寻找的。​​span>
  • 非常有用的示例,我发现的唯一一个显示子标签完全呈现自身(包括封闭标签、属性、前/后内容等),而不仅仅是添加标签的内容到上下文中,并根据父级添加封闭标记。我使用WriteTo 将标签呈现为HTML 字符串,将字符串保存在上下文中的List&lt;string&gt; 中,父标签只是将每个字符串附加到其内容中。
猜你喜欢
  • 2020-12-08
  • 1970-01-01
  • 2021-03-14
  • 2019-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-18
相关资源
最近更新 更多