Improve `HtmlHelper` extension points
- address all of #659 and a bit of #874 (avoid `public virtual` methods in `HtmlHelper`) - make `MetadataProvider` and `GetClientValidationRules()` `public` and therefore available to extension methods - remove unused `GetValidationAttributes()` overload - make remaining `GetValidationAttributes()` overload (and not `GetClientValidationRules()`) `virtual`, allowing derived classes to change the attributes without overriding all callers - reverse `GetValidationAttributes()` and `GetClientValidationRules()` parameter order to match precedence - add `GenerateName()` and `GenerateValidationSummary()` to make `protected virtual` method names consistent - `Name()`, `ValidationSummary()` and `TextArea()` are no longer `virtual` because `protected virtual Generate*()` methods exist for all
This commit is contained in:
parent
b8960219b4
commit
40eb05f7e4
|
|
@ -100,7 +100,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
protected IModelMetadataProvider MetadataProvider { get; private set; }
|
||||
/// <inheritdoc />
|
||||
public IModelMetadataProvider MetadataProvider { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public HtmlString ActionLink(
|
||||
|
|
@ -328,10 +329,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual HtmlString Name(string name)
|
||||
public HtmlString Name(string name)
|
||||
{
|
||||
var fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
|
||||
return new HtmlString(Encode(fullName));
|
||||
return GenerateName(name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -449,85 +449,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual HtmlString ValidationSummary(bool excludePropertyErrors,
|
||||
public HtmlString ValidationSummary(bool excludePropertyErrors,
|
||||
string message,
|
||||
IDictionary<string, object> htmlAttributes,
|
||||
string tag)
|
||||
{
|
||||
var formContext = ViewContext.ClientValidationEnabled ? ViewContext.FormContext : null;
|
||||
if (ViewData.ModelState.IsValid && (formContext == null || excludePropertyErrors))
|
||||
{
|
||||
// No client side validation/updates
|
||||
return HtmlString.Empty;
|
||||
}
|
||||
|
||||
string wrappedMessage;
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
if (string.IsNullOrEmpty(tag))
|
||||
{
|
||||
tag = ViewContext.ValidationSummaryMessageElement;
|
||||
}
|
||||
var messageTag = new TagBuilder(tag);
|
||||
messageTag.SetInnerText(message);
|
||||
wrappedMessage = messageTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
wrappedMessage = null;
|
||||
}
|
||||
|
||||
// If excludePropertyErrors is true, describe any validation issue with the current model in a single item.
|
||||
// Otherwise, list individual property errors.
|
||||
var htmlSummary = new StringBuilder();
|
||||
var modelStates = ValidationHelpers.GetModelStateList(ViewData, excludePropertyErrors);
|
||||
|
||||
foreach (var modelState in modelStates)
|
||||
{
|
||||
foreach (var modelError in modelState.Errors)
|
||||
{
|
||||
var errorText = ValidationHelpers.GetUserErrorMessageOrDefault(modelError, modelState: null);
|
||||
|
||||
if (!string.IsNullOrEmpty(errorText))
|
||||
{
|
||||
var listItem = new TagBuilder("li");
|
||||
listItem.SetInnerText(errorText);
|
||||
htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (htmlSummary.Length == 0)
|
||||
{
|
||||
htmlSummary.AppendLine(HiddenListItem);
|
||||
}
|
||||
|
||||
var unorderedList = new TagBuilder("ul")
|
||||
{
|
||||
InnerHtml = htmlSummary.ToString()
|
||||
};
|
||||
|
||||
var divBuilder = new TagBuilder("div");
|
||||
divBuilder.MergeAttributes(htmlAttributes);
|
||||
|
||||
if (ViewData.ModelState.IsValid)
|
||||
{
|
||||
divBuilder.AddCssClass(HtmlHelper.ValidationSummaryValidCssClassName);
|
||||
}
|
||||
else
|
||||
{
|
||||
divBuilder.AddCssClass(HtmlHelper.ValidationSummaryCssClassName);
|
||||
}
|
||||
|
||||
divBuilder.InnerHtml = wrappedMessage + unorderedList.ToString(TagRenderMode.Normal);
|
||||
|
||||
if (formContext != null && !excludePropertyErrors)
|
||||
{
|
||||
// Inform the client where to replace the list of property errors after validation.
|
||||
divBuilder.MergeAttribute("data-valmsg-summary", "true");
|
||||
}
|
||||
|
||||
return divBuilder.ToHtmlString(TagRenderMode.Normal);
|
||||
return GenerateValidationSummary(excludePropertyErrors, message, htmlAttributes, tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -549,7 +476,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual HtmlString TextArea(string name, string value, int rows, int columns, object htmlAttributes)
|
||||
public HtmlString TextArea(string name, string value, int rows, int columns, object htmlAttributes)
|
||||
{
|
||||
var metadata = ExpressionMetadataProvider.FromStringExpression(name, ViewData, MetadataProvider);
|
||||
if (value != null)
|
||||
|
|
@ -609,14 +536,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
return null;
|
||||
}
|
||||
|
||||
protected IDictionary<string, object> GetValidationAttributes(string name)
|
||||
{
|
||||
return GetValidationAttributes(name, metadata: null);
|
||||
}
|
||||
|
||||
// Only render attributes if client-side validation is enabled, and then only if we've
|
||||
// never rendered validation for a field with this name in this form.
|
||||
protected IDictionary<string, object> GetValidationAttributes(string name, ModelMetadata metadata)
|
||||
protected virtual IDictionary<string, object> GetValidationAttributes(ModelMetadata metadata, string name)
|
||||
{
|
||||
var formContext = ViewContext.ClientValidationEnabled ? ViewContext.FormContext : null;
|
||||
if (formContext == null)
|
||||
|
|
@ -631,7 +553,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
|
||||
formContext.RenderedField(fullName, true);
|
||||
var clientRules = GetClientValidationRules(name, metadata);
|
||||
var clientRules = GetClientValidationRules(metadata, name);
|
||||
return UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(clientRules);
|
||||
}
|
||||
|
||||
|
|
@ -875,6 +797,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
htmlAttributes: htmlAttributes);
|
||||
}
|
||||
|
||||
protected virtual HtmlString GenerateName(string name)
|
||||
{
|
||||
var fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
|
||||
return new HtmlString(Encode(fullName));
|
||||
}
|
||||
|
||||
protected virtual HtmlString GeneratePassword(ModelMetadata metadata, string name, object value,
|
||||
object htmlAttributes)
|
||||
{
|
||||
|
|
@ -1043,7 +971,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
tagBuilder.MergeAttributes(GetValidationAttributes(name, metadata));
|
||||
tagBuilder.MergeAttributes(GetValidationAttributes(metadata, name));
|
||||
|
||||
return tagBuilder.ToHtmlString(TagRenderMode.Normal);
|
||||
}
|
||||
|
|
@ -1094,7 +1022,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
|
||||
tagBuilder.MergeAttribute("name", fullName, true);
|
||||
tagBuilder.MergeAttributes(GetValidationAttributes(name, metadata));
|
||||
tagBuilder.MergeAttributes(GetValidationAttributes(metadata, name));
|
||||
|
||||
// If there are any errors for a named field, we add this CSS attribute.
|
||||
if (modelState != null && modelState.Errors.Count > 0)
|
||||
|
|
@ -1212,7 +1140,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
tagBuilder.AddCssClass(ValidationInputCssClassName);
|
||||
}
|
||||
|
||||
tagBuilder.MergeAttributes(GetValidationAttributes(name, metadata));
|
||||
tagBuilder.MergeAttributes(GetValidationAttributes(metadata, name));
|
||||
|
||||
if (inputType == InputType.CheckBox)
|
||||
{
|
||||
|
|
@ -1302,6 +1230,88 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
return builder.ToHtmlString(TagRenderMode.Normal);
|
||||
}
|
||||
|
||||
protected virtual HtmlString GenerateValidationSummary(
|
||||
bool excludePropertyErrors,
|
||||
string message,
|
||||
IDictionary<string, object> htmlAttributes,
|
||||
string tag)
|
||||
{
|
||||
var formContext = ViewContext.ClientValidationEnabled ? ViewContext.FormContext : null;
|
||||
if (ViewData.ModelState.IsValid && (formContext == null || excludePropertyErrors))
|
||||
{
|
||||
// No client side validation/updates
|
||||
return HtmlString.Empty;
|
||||
}
|
||||
|
||||
string wrappedMessage;
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
if (string.IsNullOrEmpty(tag))
|
||||
{
|
||||
tag = ViewContext.ValidationSummaryMessageElement;
|
||||
}
|
||||
var messageTag = new TagBuilder(tag);
|
||||
messageTag.SetInnerText(message);
|
||||
wrappedMessage = messageTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
wrappedMessage = null;
|
||||
}
|
||||
|
||||
// If excludePropertyErrors is true, describe any validation issue with the current model in a single item.
|
||||
// Otherwise, list individual property errors.
|
||||
var htmlSummary = new StringBuilder();
|
||||
var modelStates = ValidationHelpers.GetModelStateList(ViewData, excludePropertyErrors);
|
||||
|
||||
foreach (var modelState in modelStates)
|
||||
{
|
||||
foreach (var modelError in modelState.Errors)
|
||||
{
|
||||
var errorText = ValidationHelpers.GetUserErrorMessageOrDefault(modelError, modelState: null);
|
||||
|
||||
if (!string.IsNullOrEmpty(errorText))
|
||||
{
|
||||
var listItem = new TagBuilder("li");
|
||||
listItem.SetInnerText(errorText);
|
||||
htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (htmlSummary.Length == 0)
|
||||
{
|
||||
htmlSummary.AppendLine(HiddenListItem);
|
||||
}
|
||||
|
||||
var unorderedList = new TagBuilder("ul")
|
||||
{
|
||||
InnerHtml = htmlSummary.ToString()
|
||||
};
|
||||
|
||||
var divBuilder = new TagBuilder("div");
|
||||
divBuilder.MergeAttributes(htmlAttributes);
|
||||
|
||||
if (ViewData.ModelState.IsValid)
|
||||
{
|
||||
divBuilder.AddCssClass(HtmlHelper.ValidationSummaryValidCssClassName);
|
||||
}
|
||||
else
|
||||
{
|
||||
divBuilder.AddCssClass(HtmlHelper.ValidationSummaryCssClassName);
|
||||
}
|
||||
|
||||
divBuilder.InnerHtml = wrappedMessage + unorderedList.ToString(TagRenderMode.Normal);
|
||||
|
||||
if (formContext != null && !excludePropertyErrors)
|
||||
{
|
||||
// Inform the client where to replace the list of property errors after validation.
|
||||
divBuilder.MergeAttribute("data-valmsg-summary", "true");
|
||||
}
|
||||
|
||||
return divBuilder.ToHtmlString(TagRenderMode.Normal);
|
||||
}
|
||||
|
||||
protected virtual HtmlString GenerateValue(string name, object value, string format, bool useViewData)
|
||||
{
|
||||
var fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
|
||||
|
|
@ -1336,9 +1346,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
return new HtmlString(Encode(resolvedValue));
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
string name,
|
||||
ModelMetadata metadata)
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ModelMetadata metadata,
|
||||
string name)
|
||||
{
|
||||
var actionBindingContext = _actionBindingContextProvider.GetActionBindingContextAsync(ViewContext).Result;
|
||||
metadata = metadata ??
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
|
|
@ -23,6 +24,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
/// </summary>
|
||||
string IdAttributeDotReplacement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata provider. Intended for use in <see cref="IHtmlHelper"/> extension methods.
|
||||
/// </summary>
|
||||
IModelMetadataProvider MetadataProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view bag.
|
||||
/// </summary>
|
||||
|
|
@ -245,6 +251,17 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
/// <returns>The ID of the HTML element.</returns>
|
||||
string GenerateIdFromName(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Returns information about about client validation rules for the given <paramref name="metadata"/> or
|
||||
/// <paramref name="name"/>. Intended for use in <see cref="IHtmlHelper"/> extension methods.
|
||||
/// </summary>
|
||||
/// <param name="metadata">Metadata about the <see langref="object"/> of interest.</param>
|
||||
/// <param name="name">Expression name, relative to the current model. Used to determine
|
||||
/// <see cref="ModelMetadata"/> when <paramref name="metadata"/> is <see langref="null"/>; ignored
|
||||
/// otherwise.</param>
|
||||
/// <returns>An <see cref="IEnumerable{ModelClientValidationRule}"/> containing the relevant rules.</returns>
|
||||
IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, string name);
|
||||
|
||||
/// <summary>
|
||||
/// Render an input element of type "hidden".
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue