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

Commit 19b9874

Browse files
committed
Add <select/> tag helper
- #1248
1 parent 66f626b commit 19b9874

File tree

3 files changed

+166
-0
lines changed

3 files changed

+166
-0
lines changed

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

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

0 commit comments

Comments
 (0)