Тэг хелпер, порядок исполнения - PullRequest
1 голос
/ 17 июня 2019

Я пишу набор помощников по тегам ASP.Net Core, которые нацелены (среди других тегов) на теги <form> и <input>.Мой хелпер тегов <form> определяет пользовательский атрибут, значение которого он хочет передать дочерним элементам.

Все статьи, которые я прочитал, делают этот звук простым: хелпер родительского тега хранит значениев словаре context.Items, и дети читают его из того же словаря.

Это означает, что помощники дочерних тегов выполняются после помощника родительского тега. Однако я обнаружил, чтов случае помощников тегов <form> и <input> 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 пуст.

Что это такое, и что я могу сделать, чтобы обойти этот странный порядок исполнения наизнанку?

1 Ответ

0 голосов
/ 17 июня 2019

Решение

Помимо метода 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");
}

Объяснение

Для вашего 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, и это совершенно особенное.У него есть два базовых словаря ReadDictionary и WriteDictionary, и он вызывает любой из них в зависимости от того, какой тип действия (чтение / запись) выполняется в данный момент.

Хотя вы можете добавить 1 из FormTagHelper.Init()вы не сможете получить доступ к ключам 2 и 3 из FormTagHelper.Process(), несмотря на то, что согласно диаграмме они уже должны быть там:

desc

Это потому, что значения для InputTagHelper добавляются к _innerDictionary, а не _sourceDictionary, который затем используется в FormTagHelper.Такое поведение создает односторонний доступ к словарю Items.Помощники тегов Children могут получить доступ к значениям, добавленным родителями, но не наоборот.

Состояние словаря Items после выполнения Init() метода InputTagHelper():

enter image description here

...