Помощник по тегам ; как изменить динамически добавленных детей

asp-items Razor «TagHelper» добавит <option> к <select> для каждого значения в SelectList. Я хочу изменить каждого из этих детей.

В частности, я хочу отключить некоторые из них (например, добавить disabled="disabled").

Более конкретно, я хочу динамически отключать некоторые из них; Я использую angular, чтобы я мог ng-disabled="{dynamic_boolean_which_determines_disabled}". Это означает, что параметр может быть отключен сначала, но после того, как пользователь внесет изменения, параметр может быть отключен (без перезагрузки страницы). Angular должен позаботиться об этом; Я думаю, что Angular и TagHelpers теоретически должны работать вместе...

Я ожидал:

Я мог бы каким-то образом получить доступ к IEnumerable дочерних тегов <option>, которые будут созданы (т.е. по одному для каждого элемента в SelectList), перебирать дочерние теги и SetAttribute("disabled") или SetAttribute("ng-disabled")...

Я пытался:

  1. Создание моего собственного TagHelper, который нацелен на select[asp-items] и пытается получить GetChildContentAsync() и/или SetContent для достижения тегов IEnumerable <option> и перебирать их и обрабатывать каждый, но я думаю, что это позволит мне только изменить весь InnerHtml как строку; кажется хакерским делать String.replace, но я мог бы сделать это, если это мой единственный вариант? то есть ChildrenContent.Replace("<option", "<option disabled=\"...\"")
  2. Создание моего собственного TagHelper, который нацелен на элементы option, которые являются дочерними элементами select[asp-items], чтобы я мог отдельно обрабатывать каждый. Это работает, но не для динамически добавленных <option>, созданных asp-items, оно работает только для «буквальных» тегов <option>, которые я на самом деле поместил в свою разметку cshtml.

Думаю, это сработает, но не идеально:

  1. Как я сказал выше, я думаю, что могу получить результат динамических asp-элементов TagHelper <option></option> <option></option> в виде строки и выполнить замену строки, но я предпочитаю не работать со строками напрямую...
  2. Я подозреваю (я не пробовал), что мог бы просто сделать работу asp-items сам; то есть custom-items. Но затем я воссоздаю колесо, заново выполняя работу, которую asp-items мог бы сделать за меня?

person The Red Pea    schedule 26.05.2017    source источник
comment
Просто упомяну @Daniel J.G., потому что они, похоже, много знают. Но я предполагаю, что @ing этого пользователя может работать не так, как я ожидаю :)   -  person The Red Pea    schedule 26.05.2017


Ответы (1)


Так что я еще не читал "AutoLinkHttpTagHelper" в примере, который использует замену строки (в частности, замену RegEx) для замены каждого вхождения URL-адреса на <a>, указывающий на этот URL-адрес. Случаи немного разные*, но...

Во всяком случае, вот мое решение, как только я научился перестать беспокоиться и любить модификацию строки:

[HtmlTargetElement("select", Attributes = "asp-items")]
public class AspItemsNgDisabledTagHelper : SelectTagHelper
{
    //Need it to process *after* the SelectTagHelper
    public override int Order { get; } = int.MaxValue;

    //https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring#ProcessAsync
    public AspItemsNgDisabledTagHelper(IHtmlGenerator gen) : base(gen) {}

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        //Notice I'm getting the PostContent; 
        //SelectTagHelper leaves its literal content (i.e. in your CSHTML, if there is any) alone ; that's Content
        //it only **appends** new options specified; that's PostContent
        //Makes sense, but I still wasn't expecting it
        var generated_options = output.PostContent.GetContent();

        //Note you do NOT need to extend SelectTagHelper as I've done here
        //I only did it to take advantage of the asp-for property, to get its Name, so I could pass that to the angular function
        var select_for = this.For.Name;

        //The heart of the processing is a Regex.Replace, just like
        //their example https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring#inspecting-and-retrieving-child-content
        var ng_disabled_generated_options = Regex.Replace(
            generated_options,
            "<option value=\"(\\w+)\">",
            $"<option value=\"$1\" ng-disabled=\"is_disabled('{select_for}', '$1')\">");

        //Finally, you Set your modified Content
        output.PostContent.SetHtmlContent(ng_disabled_generated_options);

    }

}

Мало возможностей для обучения:

  1. Was thinking I'd find AspForTagHelper and AspItemsTagHelper, (angular background suggested that the corresponding attributes; asp-for and asp-items, would be separate "directives" aka TagHelper).
    1. In fact, TagHelper "matching" focuses on the element name (unlike angular which can match element name... attribute... class... CSS selector)
    2. Поэтому я нашел то, что искал, в SelectTagHelper, который имеет For и Items в качестве свойств. Имеет смысл.
  2. As I said above, I extend SelectTagHelper, but that's not necessary to answer my original question. It's only necessary if you want access to the this.For.Name as I've done, but there may even be a way around that (i.e. re-bind its own For property here?)
    1. I got on a distraction thinking I would need to override the SelectTagHelper's behavior to achieve my goals; i.e. Object-Oriented Thinking. In fact, even if I did extend SelectTagHelper, that doesn't stop a separate instance of the base SelectTagHelper from matching and processing the element. In other words, element processing happens in a pipeline.
    2. Это объясняет, почему расширение и вызов base.Process() приведет к тому, что Select выполнит свою работу дважды; один раз, когда совпадает ваш экземпляр, и еще раз, когда совпадает базовый экземпляр.
    3. (Я полагаю, можно было бы предотвратить сопоставление SelectTagHelper, создав новое имя элемента, например <asp-items-select>? Но это не обязательно... Я просто избегаю вызова base.Process(). Так что, если это не плохая практика...)

* Отличается следующим образом:

  1. They want to create a tag where none exists, whereas I want to add an attribute a tag which is already there; i.e. the <option>
    1. Though the <option> "tag" is generated by the SelectTagHelper in its PostContent (was expecting to find it in Content), and I don't think tags-generated-in-strings-by-content-mods can be matched with their corresponding TagHelper -- so maybe we really are the same in that we're just dealing with plain old strings
  2. Their "data" aka "model" is implied in the text itself; they find a URL and that URL string becomes a unit of meaning they use. In my case, there is an explicit class for Modeling; the SelectList (<select>) which consists of some SelectListItem (<option>) -- but that class doesn't help me either.
    1. That class only gives me attributes like public bool Disabled (remember, this isn't sufficient for me because the value of disabled could change to true or false within browser; i.e. client-side only), and public SelectListGroup Group -- certainly nothing as nonstandard as ng-disabled, nor a "catch-all" property like Attributes which could let me put arbitrary attributes (ng-disabled or anything else) in there.
person The Red Pea    schedule 26.05.2017