Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit 2fd51c8

Browse files
committed
Add <select/> tag helper
- #1248
1 parent 557974b commit 2fd51c8

File tree

4 files changed

+159
-6
lines changed

4 files changed

+159
-6
lines changed

src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs

Lines changed: 20 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@
135135
<data name="FormTagHelper_CannotDetermineAction" xml:space="preserve">
136136
<value>Cannot determine an '{1}' for {0}. A {0} with a URL-based '{1}' must not have attributes starting with '{3}' or a '{2}' attribute.</value>
137137
</data>
138-
<data name="ValidationSummaryTagHelper_InvalidValidationSummaryValue" xml:space="preserve">
138+
<data name="SelectTagHelper_CannotDetermineContentWhenOnlyItemsSpecified" xml:space="preserve">
139+
<value>Cannot determine body for {0}. '{2}' must be null if '{1}' is null.</value>
140+
</data>
141+
<data name="TagHelpers_InvalidValue_ThreeAcceptableValues" xml:space="preserve">
139142
<value>Cannot parse '{1}' value '{2}' for {0}. Acceptable values are '{3}', '{4}' and '{5}'.</value>
140143
</data>
141144
<data name="TagHelpers_NoProvidedMetadata" xml:space="preserve">
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using Microsoft.AspNet.Mvc.ModelBinding;
9+
using Microsoft.AspNet.Mvc.Rendering;
10+
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
11+
using Microsoft.AspNet.Razor.TagHelpers;
12+
13+
namespace Microsoft.AspNet.Mvc.TagHelpers
14+
{
15+
/// <summary>
16+
/// <see cref="ITagHelper"/> implementation targeting &lt;select&gt; elements.
17+
/// </summary>
18+
[ContentBehavior(ContentBehavior.Append)]
19+
public class SelectTagHelper : TagHelper
20+
{
21+
// Protected to ensure subclasses are correctly activated. Internal for ease of use when testing.
22+
[Activate]
23+
protected internal IHtmlGenerator Generator { get; set; }
24+
25+
// Protected to ensure subclasses are correctly activated. Internal for ease of use when testing.
26+
[Activate]
27+
protected internal ViewContext ViewContext { get; set; }
28+
29+
/// <summary>
30+
/// An expression to be evaluated against the current model.
31+
/// </summary>
32+
public ModelExpression For { get; set; }
33+
34+
/// <summary>
35+
/// Specifies that multiple options can be selected at once.
36+
/// </summary>
37+
/// <remarks>
38+
/// Passed through to the generated HTML if value is <c>multiple</c>. Converted to <c>multiple</c> or absent if
39+
/// value is <c>true</c> or <c>false</c>. Other values are not acceptable. Also used to determine the correct
40+
/// "selected" attributes for generated &lt;option&gt; elements.
41+
/// </remarks>
42+
public string Multiple { get; set; }
43+
44+
/// <summary>
45+
/// A collection of <see cref="SelectListItem"/> objects used to populate the &lt;select&gt; element with
46+
/// &lt;optgroup&gt; and &lt;option&gt; elements.
47+
/// </summary>
48+
public IEnumerable<SelectListItem> Items { get; set; }
49+
50+
/// <inheritdoc />
51+
/// <remarks>Does nothing if <see cref="For"/> is <c>null</c>.</remarks>
52+
public override void Process(TagHelperContext context, TagHelperOutput output)
53+
{
54+
if (For == null)
55+
{
56+
// Regular HTML <select/> element. Just make sure Items wasn't specified.
57+
if (Items != null)
58+
{
59+
var message = Resources.FormatSelectTagHelper_CannotDetermineContentWhenOnlyItemsSpecified(
60+
"<select>",
61+
nameof(For).ToLowerInvariant(),
62+
nameof(Items).ToLowerInvariant());
63+
throw new InvalidOperationException(message);
64+
}
65+
66+
// Pass through attribute that is also a well-known HTML attribute.
67+
if (!string.IsNullOrEmpty(Multiple))
68+
{
69+
output.CopyHtmlAttribute(nameof(Multiple), context);
70+
}
71+
}
72+
else
73+
{
74+
// Note null or empty For.Name is allowed because TemplateInfo.HtmlFieldPrefix may be sufficient.
75+
// IHtmlGenerator will enforce name requirements.
76+
var metadata = For.Metadata;
77+
if (metadata == null)
78+
{
79+
throw new InvalidOperationException(Resources.FormatTagHelpers_NoProvidedMetadata(
80+
"<select>",
81+
nameof(For).ToLowerInvariant(),
82+
nameof(IModelMetadataProvider),
83+
For.Name));
84+
}
85+
86+
bool allowMultiple;
87+
if (string.IsNullOrEmpty(Multiple))
88+
{
89+
// Base allowMultiple on the instance or declared type of the expression.
90+
var realModelType = For.Metadata.RealModelType;
91+
allowMultiple =
92+
typeof(string) != realModelType && typeof(IEnumerable).IsAssignableFrom(realModelType);
93+
}
94+
else if (string.Equals(Multiple, "multiple", StringComparison.OrdinalIgnoreCase))
95+
{
96+
allowMultiple = true;
97+
98+
// Copy exact attribute name and value the user entered. Must be done prior to any copying from a
99+
// TagBuilder. Not done in next case because "true" and "false" aren't valid for the HTML 5
100+
// attribute.
101+
output.CopyHtmlAttribute(nameof(Multiple), context);
102+
}
103+
else if (!bool.TryParse(Multiple.ToLowerInvariant(), out allowMultiple))
104+
{
105+
throw new InvalidOperationException(Resources.FormatTagHelpers_InvalidValue_ThreeAcceptableValues(
106+
"<select>",
107+
nameof(Multiple).ToLowerInvariant(),
108+
Multiple,
109+
bool.FalseString.ToLowerInvariant(),
110+
bool.TrueString.ToLowerInvariant(),
111+
nameof(Multiple).ToLowerInvariant())); // acceptable value (as well as attribute name)
112+
}
113+
114+
// Ensure GenerateSelect() _never_ looks anything up in ViewData.
115+
var items = Items ?? Enumerable.Empty<SelectListItem>();
116+
117+
var tagBuilder = Generator.GenerateSelect(
118+
ViewContext,
119+
For.Metadata,
120+
optionLabel: null,
121+
name: For.Name,
122+
selectList: items,
123+
allowMultiple: allowMultiple,
124+
htmlAttributes: null);
125+
126+
if (tagBuilder != null)
127+
{
128+
output.SelfClosing = false;
129+
output.Merge(tagBuilder);
130+
}
131+
}
132+
}
133+
}
134+
}

src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummaryTagHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
4242
if (!Enum.TryParse(ValidationSummaryValue, ignoreCase: true, result: out validationSummaryValue))
4343
{
4444
throw new InvalidOperationException(
45-
Resources.FormatValidationSummaryTagHelper_InvalidValidationSummaryValue(
45+
Resources.FormatTagHelpers_InvalidValue_ThreeAcceptableValues(
4646
"<div>",
4747
"validation-summary",
4848
ValidationSummaryValue,

0 commit comments

Comments
 (0)