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 <select> 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 <option> elements.
41
+ /// </remarks>
42
+ public string Multiple { get ; set ; }
43
+
44
+ /// <summary>
45
+ /// A collection of <see cref="SelectListItem"/> objects used to populate the <select> element with
46
+ /// <optgroup> and <option> 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
+ }
0 commit comments