标签助手执行顺序

Bob*_*lth 4 asp.net-core asp.net-core-tag-helpers

我正在编写一组针对(以及其他标签)<form><input>标签的 ASP.Net Core 标签助手。我的<form>标签助手定义了一个自定义属性,它想要传递给子元素的值。

我读过的所有文章都让这听起来很简单:父标签助手将值存储在context.Items字典中,孩子们从同一个字典中读取它。

这意味着子标签助手在父标签助手之后执行。然而,我发现,在案件<form><input>标签帮手,在FormTagHelper执行InputTagHelper

例如,考虑这个 HTML:

<form my-attr='Hello'>
  <input asp-for='SomeProperty' />
</form>
Run Code Online (Sandbox Code Playgroud)

我的表单标签助手:

public class FormTagHelper : TagHelper
{
    public string MyAttr { get; set; }
    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        Debug.WriteLine("<form>");
        context.Items.Add("my-attr", MyAttr ?? "");
    }
}
Run Code Online (Sandbox Code Playgroud)

输入标签助手:

public class InputTagHelper : TagHelper
{
    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        Debug.WriteLine("<input>");
        var valueFromParentForm = context.Items["my-attr"].ToString();
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望valueFromParentForm"Hello",但实际上它会抛出异常,因为 context.Items 字典是空的。

这是怎么回事,我可以做些什么来解决这个奇怪的、由内而外的执行顺序?

Pro*_*log 5

解决方案

除了Process()方法之外,基本标签助手还提供了Init()方法。总结如下:

使用Microsoft.AspNetCore.Razor.TagHelpers.ITagHelper给定的上下文初始化。补充Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext.Items应在此方法中进行,以确保他们之前执行的孩子正在增加。

只需覆盖此方法并添加您需要的任何内容:

public override void Init(TagHelperContext context)
{
    context.Items.Add(1, "Init FormTagHelper");
}
Run Code Online (Sandbox Code Playgroud)

解释

对于您的 html 代码:

<form my-attr='Hello'>
  <input asp-for='SomeProperty' />
</form>
Run Code Online (Sandbox Code Playgroud)

让我们有两个标签助手:

表单标签助手

[HtmlTargetElement("form")]
public class FormTagHelper : TagHelper
{
    public override void Init(TagHelperContext context)
    {
        context.Items.Add(1, "Init FormTagHelper");
    }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        context.Items.Add(4, "Process FormTagHelper");
    }
}
Run Code Online (Sandbox Code Playgroud)

输入标签助手

[HtmlTargetElement("input")]
public class InputTagHelper : TagHelper
{
    public override void Init(TagHelperContext context)
    {
        context.Items.Add(2, "Init InputTagHelper");
    }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        context.Items.Add(3, "Process InputTagHelper");
    }
}
Run Code Online (Sandbox Code Playgroud)

为了更好地理解调用方法的顺序,让我们看一下这张图:

TagHelpers 执行时间表

我认为执行顺序是不言自明的。但是红色No access部分呢?让我们从确定Items字典到底是什么以及它是如何工作的开始。它的数字是,IDictionary<object, object>但它不是一本普通的字典。这是一个CopyOnWriteDictionary,它很特别。它有两个底层字典ReadDictionaryWriteDictionary并且它根据当前执行的操作类型(读/写)调用它们中的任何一个。

虽然您可以添加1from FormTagHelper.Init(),但您将无法访问密钥23fromFormTagHelper.Process()尽管根据图表它们应该已经存在:

描述

那是因为 的值InputTagHelper被添加到_innerDictionarynot_sourceDictionary然后在FormTagHelper. 这种行为创建了对Items字典的单向访问。儿童标签助手能够访问父母添加的值,但不能以相反的方式访问。

Items执行Init()方法后的字典状态InputTagHelper()

在此处输入图片说明

  • 在很多层面上,ms-docs 中缺少的部分 - 感谢@Prolog (2认同)