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