diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs index d45ba4dc74..145a8039ab 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs @@ -107,19 +107,35 @@ namespace Microsoft.AspNet.Mvc.TagHelpers } /// - /// Cannot parse '{1}' value '{2}' for {0}. Acceptable values are '{3}', '{4}' and '{5}'. + /// Cannot determine body for {0}. '{2}' must be null if '{1}' is null. /// - internal static string ValidationSummaryTagHelper_InvalidValidationSummaryValue + internal static string SelectTagHelper_CannotDetermineContentWhenOnlyItemsSpecified { - get { return GetString("ValidationSummaryTagHelper_InvalidValidationSummaryValue"); } + get { return GetString("SelectTagHelper_CannotDetermineContentWhenOnlyItemsSpecified"); } + } + + /// + /// Cannot determine body for {0}. '{2}' must be null if '{1}' is null. + /// + internal static string FormatSelectTagHelper_CannotDetermineContentWhenOnlyItemsSpecified(object p0, object p1, object p2) + { + return string.Format(CultureInfo.CurrentCulture, GetString("SelectTagHelper_CannotDetermineContentWhenOnlyItemsSpecified"), p0, p1, p2); } /// /// Cannot parse '{1}' value '{2}' for {0}. Acceptable values are '{3}', '{4}' and '{5}'. /// - internal static string FormatValidationSummaryTagHelper_InvalidValidationSummaryValue(object p0, object p1, object p2, object p3, object p4, object p5) + internal static string TagHelpers_InvalidValue_ThreeAcceptableValues { - return string.Format(CultureInfo.CurrentCulture, GetString("ValidationSummaryTagHelper_InvalidValidationSummaryValue"), p0, p1, p2, p3, p4, p5); + get { return GetString("TagHelpers_InvalidValue_ThreeAcceptableValues"); } + } + + /// + /// Cannot parse '{1}' value '{2}' for {0}. Acceptable values are '{3}', '{4}' and '{5}'. + /// + internal static string FormatTagHelpers_InvalidValue_ThreeAcceptableValues(object p0, object p1, object p2, object p3, object p4, object p5) + { + return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_InvalidValue_ThreeAcceptableValues"), p0, p1, p2, p3, p4, p5); } /// diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx b/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx index b37c0c064b..c76ee1c35c 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx @@ -135,7 +135,10 @@ Cannot determine an '{1}' for {0}. A {0} with a URL-based '{1}' must not have attributes starting with '{3}' or a '{2}' attribute. - + + Cannot determine body for {0}. '{2}' must be null if '{1}' is null. + + Cannot parse '{1}' value '{2}' for {0}. Acceptable values are '{3}', '{4}' and '{5}'. diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/SelectTagHelper.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/SelectTagHelper.cs new file mode 100644 index 0000000000..6a6acc50b2 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/SelectTagHelper.cs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.Rendering; +using Microsoft.AspNet.Razor.Runtime.TagHelpers; +using Microsoft.AspNet.Razor.TagHelpers; + +namespace Microsoft.AspNet.Mvc.TagHelpers +{ + /// + /// implementation targeting <select> elements. + /// + [ContentBehavior(ContentBehavior.Append)] + public class SelectTagHelper : TagHelper + { + // Protected to ensure subclasses are correctly activated. Internal for ease of use when testing. + [Activate] + protected internal IHtmlGenerator Generator { get; set; } + + // Protected to ensure subclasses are correctly activated. Internal for ease of use when testing. + [Activate] + protected internal ViewContext ViewContext { get; set; } + + /// + /// An expression to be evaluated against the current model. + /// + public ModelExpression For { get; set; } + + /// + /// Specifies that multiple options can be selected at once. + /// + /// + /// Passed through to the generated HTML if value is multiple. Converted to multiple or absent if + /// value is true or false. Other values are not acceptable. Also used to determine the correct + /// "selected" attributes for generated <option> elements. + /// + public string Multiple { get; set; } + + /// + /// A collection of objects used to populate the <select> element with + /// <optgroup> and <option> elements. + /// + public IEnumerable Items { get; set; } + + /// + /// Does nothing if is null. + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (For == null) + { + // Regular HTML ", + nameof(For).ToLowerInvariant(), + nameof(Items).ToLowerInvariant()); + throw new InvalidOperationException(message); + } + + // Pass through attribute that is also a well-known HTML attribute. + if (!string.IsNullOrEmpty(Multiple)) + { + output.CopyHtmlAttribute(nameof(Multiple), context); + } + } + else + { + // Note null or empty For.Name is allowed because TemplateInfo.HtmlFieldPrefix may be sufficient. + // IHtmlGenerator will enforce name requirements. + var metadata = For.Metadata; + if (metadata == null) + { + throw new InvalidOperationException(Resources.FormatTagHelpers_NoProvidedMetadata( + "", + nameof(Multiple).ToLowerInvariant(), + Multiple, + bool.FalseString.ToLowerInvariant(), + bool.TrueString.ToLowerInvariant(), + nameof(Multiple).ToLowerInvariant())); // acceptable value (as well as attribute name) + } + + // Ensure GenerateSelect() _never_ looks anything up in ViewData. + var items = Items ?? Enumerable.Empty(); + + var tagBuilder = Generator.GenerateSelect( + ViewContext, + For.Metadata, + optionLabel: null, + name: For.Name, + selectList: items, + allowMultiple: allowMultiple, + htmlAttributes: null); + + if (tagBuilder != null) + { + output.SelfClosing = false; + output.Merge(tagBuilder); + } + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummaryTagHelper.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummaryTagHelper.cs index 0bb03e4298..e479c75b08 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummaryTagHelper.cs +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/ValidationSummaryTagHelper.cs @@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers if (!Enum.TryParse(ValidationSummaryValue, ignoreCase: true, result: out validationSummaryValue)) { throw new InvalidOperationException( - Resources.FormatValidationSummaryTagHelper_InvalidValidationSummaryValue( + Resources.FormatTagHelpers_InvalidValue_ThreeAcceptableValues( "
", "validation-summary", ValidationSummaryValue,