Выберите опцию в элементе select с помощниками тегов - PullRequest
0 голосов
/ 16 апреля 2020

Это моя ситуация: в базовом приложении ASP. NET мне нужно сгенерировать следующий код HTML в представлении Razor из экземпляра модели:

<select name="CultureName">
    <option value="de-DE" data-summary="Deutsch (Deutschland)">* Deutsch (Deutschland)</option>
    <option value="de-AT" data-summary="Deutsch (Österreich)">* Deutsch (Österreich)</option>
    <option value="de-CH" data-summary="Deutsch (Schweiz)">* Deutsch (Schweiz)</option>
    <option value="en-GB" data-summary="Englisch (Großbritannien)" selected>Englisch (Großbritannien)</option>
                                                                   ^^^^^^^^- important!
    <option value="en-US" data-summary="Englisch (USA)">Englisch (USA)</option>
</select>

The CultureName свойство модели имеет значение «en-GB». Модель также имеет список всех доступных опций. Отдельный атрибут data-summary поддерживается структурой веб-интерфейса, которую я использую, и должен быть установлен для каждого элемента <option>, наряду с другими атрибутами, оставленными здесь для краткости. Один из параметров предварительно выбирается атрибутом selected.

Я не могу использовать предопределенный класс SelectListItem, поскольку он не поддерживает дополнительные атрибуты. Поэтому я создал свой собственный класс SelectListOption, который выглядит аналогично, но имеет дополнительные свойства. У меня есть помощник по тегам, который позволяет мне написать это:

<select asp-for="CultureName">
    @foreach (var culture in Model.Cultures)
    {
        <option item="@culture"></option>
    }
</select>

Вот код:

[HtmlTargetElement("option", ParentTag = "select", Attributes = "item")]
public class OptionTagHelper : TagHelper
{
    public SelectListOption Item { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        if (Item != null)
        {
            output.Content.SetContent(Item.Text);
            output.Attributes.SetAttribute("value", Item.Value);
            if (Item.Summary != null)
                output.Attributes.SetAttribute("data-summary", Item.Summary);
            if (Item.HtmlText != null)
                output.Attributes.SetAttribute("data-html", Item.HtmlText);
            if (Item.HtmlSummary != null)
                output.Attributes.SetAttribute("data-summary-html", Item.HtmlSummary);
            if (Item.Selected)
                output.Attributes.SetAttribute("selected", true);
            if (Item.Disabled)
                output.Attributes.SetAttribute("disabled", true);
            output.Attributes.RemoveAll("item");
        }
    }
}

Он заполняет другие атрибуты элемента <option> из модели. Это хорошо работает, но выбор игнорируется при визуализации страницы. Таким образом, пользователь увидит список со всеми параметрами, но текущий выбор отсутствует .

Я обнаружил, что то, что я делаю, не обязательно правильно, и я должен сделать это вместо этого:

<select asp-for="CultureName" asp-items="@Model.Cultures">
</select>

Предопределенный помощник тега select затем заполнит параметры для меня. Но опять же, я не могу использовать это, потому что мне нужны дополнительные стандартные атрибуты здесь. Поэтому я тоже попытался сделать это сам.

Вот код, но я понятия не имею, что я там делаю, потому что документации об этом немного:

[HtmlTargetElement("select", Attributes = "options")]
public class SelectTagHelper : TagHelper
{
    public IEnumerable<SelectListOption> Options { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        if (Options != null)
        {
            foreach (var option in Options)
            {
                // TODO: Use HtmlEncoder.HtmlEncode() on values?
                output.Content.AppendHtml($"<option value=\"{option.Value}\"");
                if (option.Summary != null)
                    output.Content.AppendHtml($" data-summary=\"{option.Summary}\"");
                if (option.HtmlText != null)
                    output.Content.AppendHtml($" data-html=\"{option.HtmlText}\"");
                if (option.HtmlSummary != null)
                    output.Content.AppendHtml($" data-summary-html=\"{option.HtmlSummary}\"");
                if (option.Selected)
                    output.Content.AppendHtml(" selected");
                if (option.Disabled)
                    output.Content.AppendHtml(" disabled");
                output.Content.AppendHtml($">{option.Text}</option>");
            }
        }
    }
}

I ' используя это как:

<select asp-for="CultureName" options="@Model.Cultures">
</select>

Кажется, это работает, я получаю все опции в моем окне выбора. Но, конечно, теперь все еще без первоначального выбора .

Теперь я застрял. Как я могу получить атрибут selected для правильного элемента <option>? Как узнать текущее значение модели элемента <select>? У него есть атрибут asp-for, но это только имя, а не значение, которое мне нужно сопоставить.

Я ищу что-то вроде этого:

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        if (Options != null)
        {
            foreach (var option in Options)
            {
                output.Content.AppendHtml($"<option value=\"{option.Value}\"");
                if (option.Value == CURRENT_SELECT_VALUE_FROM_MODEL)
                    output.Content.AppendHtml(" selected");
                // (Other attributes)
                output.Content.AppendHtml($">{option.Text}</option>");
            }
        }
    }

Далее у меня есть другое требование : В некоторых местах мне нужно добавить еще один атрибут c, определяющий страницу, ко всем <option> элементам, которые будут использоваться для других частей формы:

<select asp-for="CultureName">
    @foreach (var culture in Model.Cultures)
    {
        <option item="@culture" data-separator="@culture.ValueObject.TextInfo.ListSeparator"></option>
    }
</select>

Это делает доступным каждый элемент CultureInfo TextInfo.ListSeparator значение для скриптов страницы через атрибут data-separator. Это, очевидно, больше не может быть обработано регистром по умолчанию, поэтому мне все еще нужно создать элементы <option> в al oop самостоятельно. К счастью, это уже описано моим первым помощником по тегам, поэтому все дополнительные атрибуты уже есть. Но опять же, атрибут selected отсутствует .

Хотя я мог попытаться изменить список параметров в модели, чтобы у одного из них было установлено свойство Selected, чтобы Это большая глупая работа, которая уже должна быть покрыта чем-то вроде asp-for.

Здесь я также должен выяснить у каждого помощника тега option, должна ли быть выбрана конкретная опция c.

Или, в качестве альтернативы, помощник по тегу select (родительский) может найти элемент option для выбора после обработки его содержимого. Поэтому, если это возможно, мне нужно будет вызвать закрывающий тег </select> и выполнить итерацию всех дочерних элементов <option>, чтобы они соответствовали их атрибуту value, и добавить атрибут selected к одному из них.

Может тегировать помощники даже помогают мне с этим или они слишком ограничены для этой задачи? Насколько помогают эти помощники?

1 Ответ

1 голос
/ 16 апреля 2020

Вы ничего не делаете с asp-for. Это не волхвы c. Если вы посмотрите на код для одного из встроенных помощников тегов, вы увидите, что у них есть свойство для захвата этого, в соответствии с:

[HtmlAttributeName("asp-for")]
public ModelExpression For { get; set; }

Затем вы должны использовать это свойство для фактического установите выбранную опцию, прочитав значение. Тем не менее, вы, вероятно, будете лучше обслуживаться, просто унаследовав свой помощник по тегам от SelectTagHelper, и просто переопределив Process для создания своей собственной логики c. Не забудьте также позвонить base.Process.

...