diff --git a/samples/MvcSample.Web/Models/User.cs b/samples/MvcSample.Web/Models/User.cs
index 1cc9b93066..015a9ce00d 100644
--- a/samples/MvcSample.Web/Models/User.cs
+++ b/samples/MvcSample.Web/Models/User.cs
@@ -8,6 +8,7 @@ namespace MvcSample.Web.Models
[MinLength(4)]
public string Name { get; set; }
public string Address { get; set; }
+ [Range(27, 70)]
public int Age { get; set; }
public decimal GPA { get; set; }
public User Dependent { get; set; }
diff --git a/samples/MvcSample.Web/Views/Home/Create.cshtml b/samples/MvcSample.Web/Views/Home/Create.cshtml
index f07cba929a..b93085eba6 100644
--- a/samples/MvcSample.Web/Views/Home/Create.cshtml
+++ b/samples/MvcSample.Web/Views/Home/Create.cshtml
@@ -50,6 +50,9 @@
|
diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs
index e799eef1d7..ac65526509 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs
@@ -416,6 +416,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
return new HtmlString(value == null ? null : value.ToString());
}
+ ///
+ public HtmlString ValidationMessage(string expression, string message, object htmlAttributes)
+ {
+ return GenerateValidationMessage(expression, message, htmlAttributes);
+ }
+
///
public virtual HtmlString ValidationSummary(bool excludePropertyErrors, string message, IDictionary htmlAttributes)
{
@@ -508,46 +514,6 @@ namespace Microsoft.AspNet.Mvc.Rendering
return divBuilder.ToHtmlString(TagRenderMode.Normal);
}
- public HtmlString ValidationMessage(string name, string message, object htmlAttributes)
- {
- ModelState modelState;
- ViewData.ModelState.TryGetValue(name, out modelState);
-
- ModelErrorCollection errors = null;
- if (modelState != null)
- {
- errors = modelState.Errors;
- }
-
- bool hasError = errors != null && errors.Any();
- if (!hasError && !ViewContext.UnobtrusiveJavaScriptEnabled)
- {
- return null;
- }
- else
- {
- string error = null;
- if (hasError)
- {
- error = message ?? errors.First().ErrorMessage;
- }
-
- var tagBuilder = new TagBuilder("span") { InnerHtml = Encode(error) };
- tagBuilder.MergeAttributes(AnonymousObjectToHtmlAttributes(htmlAttributes));
-
- if (ViewContext.UnobtrusiveJavaScriptEnabled)
- {
- bool replaceValidationMessageContents = string.IsNullOrEmpty(message);
- tagBuilder.MergeAttribute("data-valmsg-for", name);
- tagBuilder.MergeAttribute("data-valmsg-replace",
- replaceValidationMessageContents.ToString().ToLowerInvariant());
- }
-
- tagBuilder.AddCssClass(hasError ? ValidationMessageCssClassName : ValidationMessageValidCssClassName);
- return tagBuilder.ToHtmlString(TagRenderMode.Normal);
- }
- }
-
///
/// Returns the HTTP method that handles form input (GET or POST) as a string.
///
@@ -698,7 +664,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var resolvedDisplayName = metadata.PropertyName;
if (resolvedDisplayName == null)
{
- resolvedDisplayName = string.IsNullOrEmpty(htmlFieldName) ?
+ resolvedDisplayName = string.IsNullOrEmpty(htmlFieldName) ?
string.Empty :
htmlFieldName.Split('.').Last();
}
@@ -823,7 +789,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
return new HtmlString(Encode(ViewData.TemplateInfo.GetFullHtmlFieldName(expression)));
}
- protected virtual HtmlString GenerateLabel([NotNull] ModelMetadata metadata,
+ protected virtual HtmlString GenerateLabel([NotNull] ModelMetadata metadata,
string htmlFieldName,
string labelText,
object htmlAttributes)
@@ -832,7 +798,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
string resolvedLabelText = labelText ?? metadata.PropertyName;
if (resolvedLabelText == null)
{
- resolvedLabelText = string.IsNullOrEmpty(htmlFieldName) ?
+ resolvedLabelText = string.IsNullOrEmpty(htmlFieldName) ?
string.Empty :
htmlFieldName.Split('.').Last();
}
@@ -1220,6 +1186,76 @@ namespace Microsoft.AspNet.Mvc.Rendering
return tagBuilder.ToHtmlString(TagRenderMode.SelfClosing);
}
+ protected virtual HtmlString GenerateValidationMessage(string expression, string message,
+ object htmlAttributes)
+ {
+ var modelName = ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
+ if (string.IsNullOrEmpty(modelName))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "expression");
+ }
+
+ var formContext = ViewContext.GetFormContextForClientValidation();
+
+ if (!ViewData.ModelState.ContainsKey(modelName) && formContext == null)
+ {
+ return null;
+ }
+
+ ModelState modelState;
+ var tryGetModelStateResult = ViewData.ModelState.TryGetValue(modelName, out modelState);
+ var modelErrors = tryGetModelStateResult ? modelState.Errors : null;
+
+ ModelError modelError = null;
+ if(modelErrors != null && modelErrors.Count != 0)
+ {
+ modelError = modelErrors.FirstOrDefault(m => !string.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0];
+ }
+
+ if (modelError == null && formContext == null)
+ {
+ return null;
+ }
+
+ // Even if there are no model errors, we generate the span and add the validation message
+ // if formContext is not null.
+ var builder = new TagBuilder("span");
+ builder.MergeAttributes(AnonymousObjectToHtmlAttributes(htmlAttributes));
+
+ // Only the style of the span is changed according to the errors if message is null or empty.
+ // Otherwise the content and style is handled by the client-side validation.
+ builder.AddCssClass((modelError != null) ?
+ ValidationMessageCssClassName :
+ ValidationMessageValidCssClassName);
+
+ if (!string.IsNullOrEmpty(message))
+ {
+ builder.SetInnerText(message);
+ }
+ else if (modelError != null)
+ {
+ builder.SetInnerText(ValidationHelpers.GetUserErrorMessageOrDefault(modelError, modelState));
+ }
+
+ if (formContext != null)
+ {
+ var replaceValidationMessageContents = string.IsNullOrEmpty(message);
+
+ if (ViewContext.UnobtrusiveJavaScriptEnabled)
+ {
+ builder.MergeAttribute("data-valmsg-for", modelName);
+ builder.MergeAttribute("data-valmsg-replace",
+ replaceValidationMessageContents.ToString().ToLowerInvariant());
+ }
+
+ // TODO: (WebFX-217) Add support for Unobtrusive JS disabled -
+ // Modify the field metadata to add the validation message,
+ // Add the client validation id in the field metadata
+ }
+
+ return builder.ToHtmlString(TagRenderMode.Normal);
+ }
+
protected virtual HtmlString GenerateValue(string name, object value, string format, bool useViewData)
{
var fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs
index 8141b33a2e..d1d7e3497f 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs
@@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// Initializes a new instance of the class.
///
public HtmlHelper(
- [NotNull] IViewEngine viewEngine,
+ [NotNull] IViewEngine viewEngine,
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] IUrlHelper urlHelper,
[NotNull] AntiForgery antiForgeryInstance)
@@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
///
- public new ViewDataDictionary ViewData { get; private set;}
+ public new ViewDataDictionary ViewData { get; private set; }
public override void Contextualize([NotNull] ViewContext viewContext)
{
@@ -30,7 +30,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
throw new ArgumentException(Resources.FormatPropertyOfTypeCannotBeNull(
"ViewData",
- typeof(ViewContext)),
+ typeof(ViewContext)),
"viewContext");
}
@@ -74,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
object additionalViewData)
{
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression,
- ViewData,
+ ViewData,
MetadataProvider);
return GenerateDisplay(metadata,
@@ -212,7 +212,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
///
- public HtmlString ValueFor([NotNull] Expression> expression, string format)
+ public HtmlString ValidationMessageFor([NotNull] Expression> expression,
+ string message, object htmlAttributes)
+ {
+ return GenerateValidationMessage(ExpressionHelper.GetExpressionText(expression), message, htmlAttributes);
+ }
+
+ ///
+ public HtmlString ValueFor(Expression> expression, string format)
{
var metadata = GetModelMetadata(expression);
return GenerateValue(ExpressionHelper.GetExpressionText(expression), metadata.Model, format,
diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperValidationExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperValidationExtensions.cs
index cbef6ccb22..df70629fc5 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperValidationExtensions.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperValidationExtensions.cs
@@ -1,9 +1,53 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
namespace Microsoft.AspNet.Mvc.Rendering
{
public static class HtmlHelperValidationExtensions
{
+ public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
+ string expression)
+ {
+ return htmlHelper.ValidationMessage(expression, message: null, htmlAttributes: null);
+ }
+
+ public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
+ string expression, string message)
+ {
+ return htmlHelper.ValidationMessage(expression, message, htmlAttributes: null);
+ }
+
+ public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
+ string expression, object htmlAttributes)
+ {
+ return htmlHelper.ValidationMessage(expression, message: null, htmlAttributes: htmlAttributes);
+ }
+
+ public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
+ string expression, string message, object htmlAttributes)
+ {
+ return htmlHelper.ValidationMessage(expression, message, htmlAttributes);
+ }
+
+ public static HtmlString ValidationMessageFor([NotNull] this IHtmlHelper htmlHelper,
+ [NotNull] Expression> expression)
+ {
+ return htmlHelper.ValidationMessageFor(expression, message: null, htmlAttributes: null);
+ }
+
+ public static HtmlString ValidationMessageFor([NotNull] this IHtmlHelper htmlHelper,
+ [NotNull] Expression> expression, string message)
+ {
+ return htmlHelper.ValidationMessageFor(expression, message, htmlAttributes: null);
+ }
+
+ public static HtmlString ValidationMessageFor([NotNull] this IHtmlHelper htmlHelper,
+ [NotNull] Expression> expression, string message, object htmlAttributes)
+ {
+ return htmlHelper.ValidationMessageFor(expression, message, htmlAttributes);
+ }
+
public static HtmlString ValidationSummary([NotNull] this IHtmlHelper htmlHelper)
{
return ValidationSummary(htmlHelper, excludePropertyErrors: false);
@@ -55,41 +99,5 @@ namespace Microsoft.AspNet.Mvc.Rendering
return htmlHelper.ValidationSummary(excludePropertyErrors: false, message: message,
htmlAttributes: htmlAttributes);
}
-
- public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
- string modelName)
- {
- return ValidationMessage(htmlHelper, modelName, message: null, htmlAttributes: null);
- }
-
- public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
- string modelName, string message)
- {
- return ValidationMessage(htmlHelper, modelName, message, htmlAttributes: null);
- }
-
- public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
- string modelName, object htmlAttributes)
- {
- return ValidationMessage(htmlHelper, modelName, message: null, htmlAttributes: htmlAttributes);
- }
-
- public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
- string modelName, IDictionary htmlAttributes)
- {
- return ValidationMessage(htmlHelper, modelName, message: null, htmlAttributes: htmlAttributes);
- }
-
- public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
- string modelName, string message, IDictionary htmlAttributes)
- {
- return htmlHelper.ValidationMessage(modelName, message, htmlAttributes);
- }
-
- public static HtmlString ValidationMessage([NotNull] this IHtmlHelper htmlHelper,
- string modelName, string message, object htmlAttributes)
- {
- return htmlHelper.ValidationMessage(modelName, message, htmlAttributes);
- }
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs
index 80c37299ff..0891342236 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs
@@ -393,6 +393,18 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// New containing the rendered HTML.
HtmlString TextBox(string name, object value, string format, IDictionary htmlAttributes);
+ ///
+ /// Returns the validation message if an error exists in the object.
+ ///
+ /// The name of the property that is being validated.
+ /// The message to be displayed. This will always be visible but client-side
+ /// validation may update the associated CSS class.
+ /// An object that contains the HTML attributes to set for the element.
+ /// Alternatively, an instance containing the HTML attributes.
+ ///
+ /// An that contains the validation message
+ HtmlString ValidationMessage(string modelName, string message, object htmlAttributes);
+
///
/// Returns an unordered list (ul element) of validation messages that are in the
/// object.
diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs
index d8ecfdd115..c7619f8614 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs
@@ -224,14 +224,17 @@ namespace Microsoft.AspNet.Mvc.Rendering
IDictionary htmlAttributes);
///
- /// Returns the validation message if an error exists in the object.
+ /// Returns the validation message for the specified expression
///
- /// The name of the property that is being validated.
- /// The message to be displayed if the specified field contains an error.
- /// Dictionary that contains the HTML attributes which should
- /// be applied on the element
- ///
- HtmlString ValidationMessage(string modelName, string message, object htmlAttributes);
+ /// An expression, relative to the current model.
+ /// The message to be displayed. This will always be visible but client-side
+ /// validation may update the associated CSS class.
+ /// An object that contains the HTML attributes to set for the element.
+ /// Alternatively, an /// instance containing the HTML attributes.
+ ///
+ /// An that contains the validation message
+ HtmlString ValidationMessageFor([NotNull] Expression> expression,
+ string message, object htmlAttributes);
///
/// Returns the model value for the given expression .
diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs b/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs
index 5c49b84f2b..f551a2cbe8 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
using System.IO;
-using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Mvc.Rendering;
namespace Microsoft.AspNet.Mvc
@@ -82,5 +79,10 @@ namespace Microsoft.AspNet.Mvc
public ViewDataDictionary ViewData { get; set; }
public TextWriter Writer { get; set; }
+
+ public FormContext GetFormContextForClientValidation()
+ {
+ return (ClientValidationEnabled) ? FormContext : null;
+ }
}
}
|