在带有标签助手的选择元素中选择一个选项

时间:2020-04-16 12:54:46

标签: c# html asp.net-core razor tag-helpers

这是我的情况:在ASP.NET Core应用程序中,我需要从模型实例的Razor视图中生成以下HTML代码:

<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>

模型的CultureName属性的值为“ en-GB”。该模型还具有所有可用选项的列表。我使用的Web前端框架支持单独的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>

然后,预定义的选择标签助手将为我填写选项。但是再次,我不能使用它,因为在这里我需要其他标准属性。所以我也想自己做。

这是代码,但是我真的不知道我在做什么,因为关于它的文档并不多:

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

我将其用作:

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

下一步,我还有另一个要求:在某些地方,我需要向所有<option>元素添加另一个页面特定的属性,这些属性将用于表单的其他部分:

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

这将通过TextInfo.ListSeparator属性将每个CultureInfo的data-separator值提供给页面脚本。显然,默认情况下无法再解决此问题,因此我仍然需要自己在循环中创建<option>元素。幸运的是,我的第一个标签助手已经覆盖了它,因此所有其他属性都已经存在。但是,再次缺少 selected属性

虽然我可以尝试修改模型中的选项列表,以使其中的一个具有Selected属性集,但这是许多愚蠢的工作,应该已经被{{1} }。

在这里,我还需要从每个选项标签助手中找出是否应选择特定选项。

或者,或者,选择标签帮助器(父级)可以在处理完内容之后找到要选择的选项元素。因此,如果可能的话,我将必须触发结束asp-for标记并迭代所有</select>子元素以匹配其<option>属性,并向其中一个添加value属性。

标记助手可以帮助我吗?还是对于这项任务来说太有限了?这些助手真的有多少帮助?

1 个答案:

答案 0 :(得分:1)

您没有对asp-for做任何事情。这不是魔术。如果您查看其中一个内置标记帮助器的代码,您将看到它们具有捕获该属性的属性,类似于:

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

然后,您必须使用此属性通过读取值来实际设置所选选项。就是说,不过,最好是从SelectTagHelper继承标签帮助程序,而重写Process来执行自己的逻辑,则可能会更好。不过,请记住也要拨打base.Process

相关问题