标签助手的执行顺序
Tag helper order of execution
我正在编写一组 ASP.Net 核心标签助手,目标是(在其他标签中)<form>
和 <input>
标签。我的 <form>
标签助手定义了一个自定义属性,它希望将其值传递给子元素。
我读过的所有文章都让这听起来很简单:父标签助手将值存储在 context.Items
词典中,子级从同一个词典中读取它。
这意味着 子标签助手在父标签助手之后执行。但是,我发现,在 <form>
和 [=14 的情况下=] 标签助手,FormTagHelper
在 InputTagHelper
.
之后执行
例如,考虑这个 HTML:
<form my-attr='Hello'>
<input asp-for='SomeProperty' />
</form>
我的表单标签助手:
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 ?? "");
}
}
输入标签助手:
public class InputTagHelper : TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
Debug.WriteLine("<input>");
var valueFromParentForm = context.Items["my-attr"].ToString();
}
}
我希望 valueFromParentForm
是 "Hello"
,但实际上它抛出异常,因为 context.Items 字典是空的。
这是怎么回事,我可以做些什么来解决这个奇怪的、由内而外的执行顺序?
解决方案
留给 Process()
method the base tag helper provides also Init()
方法。摘要:
Initializes the Microsoft.AspNetCore.Razor.TagHelpers.ITagHelper
with the given context. Additions to Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext.Items
should be done within this method to ensure they're added prior to executing the children.
只需覆盖此方法并添加您需要的任何内容:
public override void Init(TagHelperContext context)
{
context.Items.Add(1, "Init FormTagHelper");
}
说明
对于您的 html 代码:
<form my-attr='Hello'>
<input asp-for='SomeProperty' />
</form>
让我们有两个标签助手:
FormTagHelper
[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");
}
}
InputTagHelper
[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");
}
}
为了更好地理解方法的调用顺序,让我们看一下这张图:
我觉得执行顺序很好self-explanatory。但是红色 No access
部分呢?让我们从确定 Items
字典到底是什么以及它如何工作开始。它显示为 IDictionary<object, object>
但它不是常规词典。这是一个CopyOnWriteDictionary
,而且很特别。它有两个基础词典 ReadDictionary
和 WriteDictionary
,它会根据当前执行的操作类型 (read/write) 调用其中一个。
虽然您可以从 FormTagHelper.Init()
添加 1
,但您将无法从 FormTagHelper.Process()
访问密钥 2
和 3
,尽管事实上根据图表,他们应该已经在那里了:
那是因为 InputTagHelper
的值被添加到 _innerDictionary
,而不是 _sourceDictionary
,然后在 FormTagHelper
中使用。这种行为创建 one-way 访问 Items
字典。子标记助手能够访问父添加的值,但不能以相反的方式访问。
执行 InputTagHelper()
的 Init()
方法后 Items
字典的状态:
我现在 运行 以下标签助手(parent 和 children)
<sp-row>
<sp-col>Child 1</sp-col>
<sp-col>Child 2</sp-col>
</sp-row>
并且它是 运行 以下顺序(而不是预览答案的顺序):
- Parent 的 Init(TagHelperContext 上下文)
- Parent 的 ProcessAsync(TagHelperContext 上下文,TagHelperOutput 输出)
- Parent的进程(TagHelperContext上下文,TagHelperOutput输出)
- Child1 的初始化(TagHelperContext 上下文)
- Child1 的 ProcessAsync(TagHelperContext 上下文,TagHelperOutput 输出)
- Child1 的进程(TagHelperContext 上下文,TagHelperOutput 输出)
- Child2 的初始化(TagHelperContext 上下文)
- Child2 的 ProcessAsync(TagHelperContext 上下文,TagHelperOutput 输出)
- Child2 的进程(TagHelperContext 上下文,TagHelperOutput 输出)
我正在编写一组 ASP.Net 核心标签助手,目标是(在其他标签中)<form>
和 <input>
标签。我的 <form>
标签助手定义了一个自定义属性,它希望将其值传递给子元素。
我读过的所有文章都让这听起来很简单:父标签助手将值存储在 context.Items
词典中,子级从同一个词典中读取它。
这意味着 子标签助手在父标签助手之后执行。但是,我发现,在 <form>
和 [=14 的情况下=] 标签助手,FormTagHelper
在 InputTagHelper
.
例如,考虑这个 HTML:
<form my-attr='Hello'>
<input asp-for='SomeProperty' />
</form>
我的表单标签助手:
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 ?? "");
}
}
输入标签助手:
public class InputTagHelper : TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
Debug.WriteLine("<input>");
var valueFromParentForm = context.Items["my-attr"].ToString();
}
}
我希望 valueFromParentForm
是 "Hello"
,但实际上它抛出异常,因为 context.Items 字典是空的。
这是怎么回事,我可以做些什么来解决这个奇怪的、由内而外的执行顺序?
解决方案
留给 Process()
method the base tag helper provides also Init()
方法。摘要:
Initializes the
Microsoft.AspNetCore.Razor.TagHelpers.ITagHelper
with the given context. Additions toMicrosoft.AspNetCore.Razor.TagHelpers.TagHelperContext.Items
should be done within this method to ensure they're added prior to executing the children.
只需覆盖此方法并添加您需要的任何内容:
public override void Init(TagHelperContext context)
{
context.Items.Add(1, "Init FormTagHelper");
}
说明
对于您的 html 代码:
<form my-attr='Hello'>
<input asp-for='SomeProperty' />
</form>
让我们有两个标签助手:
FormTagHelper
[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");
}
}
InputTagHelper
[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");
}
}
为了更好地理解方法的调用顺序,让我们看一下这张图:
我觉得执行顺序很好self-explanatory。但是红色 No access
部分呢?让我们从确定 Items
字典到底是什么以及它如何工作开始。它显示为 IDictionary<object, object>
但它不是常规词典。这是一个CopyOnWriteDictionary
,而且很特别。它有两个基础词典 ReadDictionary
和 WriteDictionary
,它会根据当前执行的操作类型 (read/write) 调用其中一个。
虽然您可以从 FormTagHelper.Init()
添加 1
,但您将无法从 FormTagHelper.Process()
访问密钥 2
和 3
,尽管事实上根据图表,他们应该已经在那里了:
那是因为 InputTagHelper
的值被添加到 _innerDictionary
,而不是 _sourceDictionary
,然后在 FormTagHelper
中使用。这种行为创建 one-way 访问 Items
字典。子标记助手能够访问父添加的值,但不能以相反的方式访问。
执行 InputTagHelper()
的 Init()
方法后 Items
字典的状态:
我现在 运行 以下标签助手(parent 和 children)
<sp-row>
<sp-col>Child 1</sp-col>
<sp-col>Child 2</sp-col>
</sp-row>
并且它是 运行 以下顺序(而不是预览答案的顺序):
- Parent 的 Init(TagHelperContext 上下文)
- Parent 的 ProcessAsync(TagHelperContext 上下文,TagHelperOutput 输出)
- Parent的进程(TagHelperContext上下文,TagHelperOutput输出)
- Child1 的初始化(TagHelperContext 上下文)
- Child1 的 ProcessAsync(TagHelperContext 上下文,TagHelperOutput 输出)
- Child1 的进程(TagHelperContext 上下文,TagHelperOutput 输出)
- Child2 的初始化(TagHelperContext 上下文)
- Child2 的 ProcessAsync(TagHelperContext 上下文,TagHelperOutput 输出)
- Child2 的进程(TagHelperContext 上下文,TagHelperOutput 输出)