标记助手执行顺序

时间:2019-06-17 06:40:41

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

我正在编写一组ASP.Net Core标记帮助器,这些辅助器以<form><input>标签为目标(以及其他标签)。我的<form>标签帮助程序定义了一个自定义属性,该属性希望将其值传递给子元素。

我读过的所有文章都听起来很简单:父标记助手将值存储在context.Items字典中,而子项则从同一字典中读取它。

这意味着子标记助手在父标记助手之后执行。但是,我发现,对于<form><input>标记助手, FormTagHelperInputTagHelper之后执行。

通过示例,请考虑以下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字典为空。

这是什么一回事,我应该怎么做才能解决这种奇怪的,由内而外的执行顺序?

2 个答案:

答案 0 :(得分:0)

解决方案

除了Process()方法外,基本标记帮助程序还提供了Init()方法。摘要:

  

使用给定的上下文初始化Microsoft.AspNetCore.Razor.TagHelpers.ITagHelperMicrosoft.AspNetCore.Razor.TagHelpers.TagHelperContext.Items的添加应在此方法内完成,以确保在执行子代之前将其添加。

只需重写此方法并添加所需的内容:

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");
    }
}

为了更好地理解按什么顺序调用方法,让我们看一下该图:

TagHelpers execution timeline

我认为执行顺序是不言自明的。但是红色的No access部分呢?让我们从建立确切的Items字典及其工作开始。它的编号为IDictionary<object, object>,但不是常规词典。这是CopyOnWriteDictionary,非常特殊。它有两个基础字典ReadDictionaryWriteDictionary,并根据当前执行的操作类型(读/写)来调用其中的一个。

尽管您可以从1添加FormTagHelper.Init(),但是尽管您可以根据以下条件访问2中的键3FormTagHelper.Process()到他们应该已经在图中的位置:

desc

这是因为InputTagHelper的值被添加到_innerDictionary而不是_sourceDictionary中,然后在FormTagHelper中使用。这种行为会创建对Items字典的单向访问。儿童标签助手可以访问父母添加的值,但不能相反。

执行Items的{​​{1}}方法后Init()字典的状态:

enter image description here

答案 1 :(得分:0)

我现在运行以下标记助手(父母和孩子)

<sp-row>
  <sp-col>Child 1</sp-col>
  <sp-col>Child 2</sp-col>
</sp-row>

,它按以下顺序运行(而不是预览答案的顺序):

  1. 父级的Init(TagHelperContext上下文)
  2. 父级的ProcessAsync(TagHelperContext上下文,TagHelperOutput输出)
  3. 父级的流程(TagHelperContext上下文,TagHelperOutput输出)
  4. Child1的Init(TagHelperContext上下文)
  5. Child1的ProcessAsync(TagHelperContext上下文,TagHelperOutput输出)
  6. Child1的流程(TagHelperContext上下文,TagHelperOutput输出)
  7. Child2的Init(TagHelperContext上下文)
  8. Child2的ProcessAsync(TagHelperContext上下文,TagHelperOutput输出)
  9. Child2的流程(TagHelperContext上下文,TagHelperOutput输出)