Select select 元素中带有标签助手的选项
Select an option in a select element with tag helpers
这是我的情况:在 ASP.NET 核心应用程序中,我需要在模型实例的 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
属性预 select 选项之一。
我无法使用预定义的 SelectListItem
class,因为它不支持附加属性。所以我创建了自己的 SelectListOption
class ,它看起来很相似但有额外的属性。我有一个标签助手,可以让我这样写:
<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>
元素的其他属性。这很好用,但是 selection 在渲染页面时被忽略了。因此用户将看到一个包含所有选项的列表,但是 当前 selection 丢失了.
我发现我做的不一定正确,我应该这样做:
<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>");
}
}
}
}
我将其用作:
<select asp-for="CultureName" options="@Model.Cultures">
</select>
这似乎可行,我在 select 框中获得了所有选项。但是当然现在仍然没有最初的selection。
现在我卡住了。如何将 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>
这使得每个 CultureInfo 的 TextInfo.ListSeparator
值通过 data-separator
属性可用于页面脚本。这显然不能再由 default case 处理,所以我仍然需要自己在循环中创建 <option>
元素。幸运的是,我的第一个标签助手已经涵盖了这一点,因此所有额外的属性都已经存在。但是同样,缺少 selected
属性 .
虽然我可以尝试修改模型中的选项列表,以便其中之一具有 Selected
属性 集,但这是很多愚蠢的工作,应该已经涵盖了像 asp-for
.
在这里,我还需要从每个选项标签助手中找出特定选项是否应该 selected。
或者,select 标签助手(父级)可以在其内容被处理后找到 select 的选项元素。因此,如果可能的话,我将不得不触发结束 </select>
标记并迭代所有 <option>
子元素以匹配它们的 value
属性并向其中之一添加 selected
属性。
标签助手甚至可以帮我解决这个问题,还是说他们对这项任务来说太有限了?这些帮手真的有多大帮助?
您没有对 asp-for
做任何事情。这不是魔术。如果您查看其中一个内置标签助手的代码,您会看到它们有 属性 用于捕获此代码,如下所示:
[HtmlAttributeName("asp-for")]
public ModelExpression For { get; set; }
然后,您必须使用此 属性 通过读取值来实际设置所选选项。尽管如此,您可能会通过简单地从 SelectTagHelper
继承标签助手并覆盖 Process
来执行您自己的逻辑来获得更好的服务。不过,请记住也要调用 base.Process
。
这是我的情况:在 ASP.NET 核心应用程序中,我需要在模型实例的 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
属性预 select 选项之一。
我无法使用预定义的 SelectListItem
class,因为它不支持附加属性。所以我创建了自己的 SelectListOption
class ,它看起来很相似但有额外的属性。我有一个标签助手,可以让我这样写:
<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>
元素的其他属性。这很好用,但是 selection 在渲染页面时被忽略了。因此用户将看到一个包含所有选项的列表,但是 当前 selection 丢失了.
我发现我做的不一定正确,我应该这样做:
<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>");
}
}
}
}
我将其用作:
<select asp-for="CultureName" options="@Model.Cultures">
</select>
这似乎可行,我在 select 框中获得了所有选项。但是当然现在仍然没有最初的selection。
现在我卡住了。如何将 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>
这使得每个 CultureInfo 的 TextInfo.ListSeparator
值通过 data-separator
属性可用于页面脚本。这显然不能再由 default case 处理,所以我仍然需要自己在循环中创建 <option>
元素。幸运的是,我的第一个标签助手已经涵盖了这一点,因此所有额外的属性都已经存在。但是同样,缺少 selected
属性 .
虽然我可以尝试修改模型中的选项列表,以便其中之一具有 Selected
属性 集,但这是很多愚蠢的工作,应该已经涵盖了像 asp-for
.
在这里,我还需要从每个选项标签助手中找出特定选项是否应该 selected。
或者,select 标签助手(父级)可以在其内容被处理后找到 select 的选项元素。因此,如果可能的话,我将不得不触发结束 </select>
标记并迭代所有 <option>
子元素以匹配它们的 value
属性并向其中之一添加 selected
属性。
标签助手甚至可以帮我解决这个问题,还是说他们对这项任务来说太有限了?这些帮手真的有多大帮助?
您没有对 asp-for
做任何事情。这不是魔术。如果您查看其中一个内置标签助手的代码,您会看到它们有 属性 用于捕获此代码,如下所示:
[HtmlAttributeName("asp-for")]
public ModelExpression For { get; set; }
然后,您必须使用此 属性 通过读取值来实际设置所选选项。尽管如此,您可能会通过简单地从 SelectTagHelper
继承标签助手并覆盖 Process
来执行您自己的逻辑来获得更好的服务。不过,请记住也要调用 base.Process
。