ASP.NET MVC3自定义验证消息行为

时间:2012-01-23 06:12:01

标签: asp.net-mvc-3 data-annotations

来自asp.net webforms模型我习惯使用以<span title="Username is Required">*</span>形式显示错误的验证器。

我很清楚MVC3验证器是如何开箱即用的,所以请不要再解释验证验证器如何在MVC3中工作,因为我很确定我已经将其固定。我想要完成的是将验证错误消息显示为第一段中显示的span标记的标题。

我已经设法在MVC3中复制了这个,但我不确定我的方式是否遵循最佳实践。我将不胜感激是否有更好的方法来完成同样的事情。如果可以在不修改jquery.validate.unobtrusive.js的情况下完成它将会非常棒。

所以我所做的是:

1)将验证消息设置为“*” 2)在有效时隐藏验证消息 3)添加了一个新属性以确定是否将消息添加为标题 4)在onError中添加了2行标记代码,以检查是否在标题中显示错误消息,如果是,则执行此操作。

[.cshtml]    @Html.ValidationMessageFor(m => m.Email, "*", new { data_val_usetitle = "true" })

[.css]    .field-validation-valid {display:none;}

.js]        function onError(error, inputElement) {  // 'this' is the form element
            var container = $(this).find("[data-valmsg-for='" + inputElement[0].name + "']"),
                replace = $.parseJSON(container.attr("data-valmsg-replace")) !== false,
                useTitle = $.parseJSON(container.attr("data-val-usetitle")) !== false; /* New Line */

            container.removeClass("field-validation-valid").addClass("field-validation-error");
            error.data("unobtrusiveContainer", container);

            if (replace) {
                container.empty();
                error.removeClass("input-validation-error").appendTo(container);
            }
            else {
                if (useTitle) container.attr("title", error.text()); /* New Line */
                error.hide();
            }
        }

2 个答案:

答案 0 :(得分:3)

我认为你所做的是最干净的方式。没有办法修改jquery.validate.unobtrusive.js,因为MVC扩展不遵循旧的asp.net方法即时发出javascript。

我刚刚创建了自己的自定义验证扩展calld ValidationIconFor(),以便显示单个图像,其标题设置为错误消息,并且我使用了上面代码的修改版本。

<强> jquery.validate.unobtrusive.js

function onError(error, inputElement) {  // 'this' is the form element
    var container = $(this).find("[data-valmsg-for='" + inputElement[0].name + "']"),
        replace = $.parseJSON(container.attr("data-valmsg-replace")) !== false,
        useTitle = $.parseJSON(container.attr("data-val-usetitle")) !== false;

    container.removeClass("field-validation-valid").addClass("field-validation-error");
    error.data("unobtrusiveContainer", container);

    if (replace) {
        container.empty();
        if (useTitle)
            container.attr("title", error.text());
        else
            error.removeClass("input-validation-error").appendTo(container);
    }
    else {
        if (useTitle)
            container.attr("title", error.text());
        error.hide();
    }
}

<强> ValidationExtensions.cs

public static class ValidationExtensions
{
    private static string _resourceClassKey;

    public static string ResourceClassKey
    {
        get
        {
            return _resourceClassKey ?? String.Empty;
        }
        set
        {
            _resourceClassKey = value;
        }
    }

    private static FieldValidationMetadata ApplyFieldValidationMetadata(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string modelName)
    {
        FormContext formContext = htmlHelper.ViewContext.FormContext;
        FieldValidationMetadata fieldMetadata = formContext.GetValidationMetadataForField(modelName, true /* createIfNotFound */);

        // write rules to context object
        IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetadata, htmlHelper.ViewContext);
        foreach (ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules()))
        {
            fieldMetadata.ValidationRules.Add(rule);
        }

        return fieldMetadata;
    }

    private static string GetInvalidPropertyValueResource(HttpContextBase httpContext)
    {
        string resourceValue = null;
        if (!String.IsNullOrEmpty(ResourceClassKey) && (httpContext != null))
        {
            // If the user specified a ResourceClassKey try to load the resource they specified.
            // If the class key is invalid, an exception will be thrown.
            // If the class key is valid but the resource is not found, it returns null, in which
            // case it will fall back to the MVC default error message.
            resourceValue = httpContext.GetGlobalResourceObject(ResourceClassKey, "InvalidPropertyValue", CultureInfo.CurrentUICulture) as string;
        }
        return resourceValue ?? "The value '{0}' is invalid.";
    }

    private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState)
    {
        if (!String.IsNullOrEmpty(error.ErrorMessage))
        {
            return error.ErrorMessage;
        }
        if (modelState == null)
        {
            return null;
        }

        string attemptedValue = (modelState.Value != null) ? modelState.Value.AttemptedValue : null;
        return String.Format(CultureInfo.CurrentCulture, GetInvalidPropertyValueResource(httpContext), attemptedValue);
    }

    [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
    public static MvcHtmlString ValidationIconFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        return ValidationIconFor(htmlHelper, expression, null /* validationMessage */, new RouteValueDictionary());
    }

    [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
    public static MvcHtmlString ValidationIconFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string validationMessage)
    {
        return ValidationIconFor(htmlHelper, expression, validationMessage, new RouteValueDictionary());
    }

    [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
    public static MvcHtmlString ValidationIconFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string validationMessage, object htmlAttributes)
    {
        return ValidationIconFor(htmlHelper, expression, validationMessage, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
    public static MvcHtmlString ValidationIconFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string validationMessage, IDictionary<string, object> htmlAttributes)
    {
        return ValidationMessageHelper(htmlHelper,
                                       ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData),
                                       ExpressionHelper.GetExpressionText(expression),
                                       validationMessage,
                                       htmlAttributes);
    }

    [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Normalization to lowercase is a common requirement for JavaScript and HTML values")]
    private static MvcHtmlString ValidationMessageHelper(this HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression, string validationMessage, IDictionary<string, object> htmlAttributes)
    {
        string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
        FormContext formContext = htmlHelper.ViewContext.FormContext;

        if (!htmlHelper.ViewData.ModelState.ContainsKey(modelName) && formContext == null)
        {
            return null;
        }

        ModelState modelState = htmlHelper.ViewData.ModelState[modelName];
        ModelErrorCollection modelErrors = (modelState == null) ? null : modelState.Errors;
        ModelError modelError = (((modelErrors == null) || (modelErrors.Count == 0)) ? null : modelErrors.FirstOrDefault(m => !String.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0]);

        if (modelError == null && formContext == null)
        {
            return null;
        }

        TagBuilder builder = new TagBuilder("img");
        builder.MergeAttributes(htmlAttributes);
        builder.AddCssClass((modelError != null) ? HtmlHelper.ValidationMessageCssClassName : HtmlHelper.ValidationMessageValidCssClassName);

        if (!String.IsNullOrEmpty(validationMessage))
        {
            builder.Attributes.Add("title", validationMessage);
        }
        else if (modelError != null)
        {
            builder.Attributes.Add("title", GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, modelState));
        }

        if (formContext != null)
        {
            bool replaceValidationMessageContents = String.IsNullOrEmpty(validationMessage);

            if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
            {
                builder.MergeAttribute("data-valmsg-for", modelName);
                builder.MergeAttribute("data-valmsg-replace", replaceValidationMessageContents.ToString().ToLowerInvariant());
                builder.MergeAttribute("data-val-usetitle", "true");
            }
            else
            {
                FieldValidationMetadata fieldMetadata = ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName);
                // rules will already have been written to the metadata object
                fieldMetadata.ReplaceValidationMessageContents = replaceValidationMessageContents; // only replace contents if no explicit message was specified

                // client validation always requires an ID
                builder.GenerateId(modelName + "_validationMessage");
                fieldMetadata.ValidationMessageId = builder.Attributes["id"];
            }
        }

        return builder.ToMvcHtmlString(TagRenderMode.Normal);
    }
}

internal static class TagBuilderExtensions
{
    internal static MvcHtmlString ToMvcHtmlString(this TagBuilder tagBuilder, TagRenderMode renderMode)
    {
        return new MvcHtmlString(tagBuilder.ToString(renderMode));
    }
}

答案 1 :(得分:0)

您发布的所有javascript和css都是由验证库为您完成的。您需要做的就是为模型添加验证属性,然后放置验证消息和摘要html / razor标记。

型号:

 public class LogOnModel
    {
        [Required]
        [Display(Name = "Username")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }
    }

查看:

                <div class="editor-label">
                    @Html.LabelFor(m => m.UserName)*
                </div>
                <div class="editor-field">
                    @Html.TextBoxFor(m => m.UserName, new { autocomplete = "off" })
                    @Html.ValidationMessageFor(m => m.UserName, "")
                </div>
                <div class="editor-label">
                    @Html.LabelFor(m => m.Password)*
                </div>
                <div class="editor-field">
                    @Html.PasswordFor(m => m.Password, new { autocomplete = "off" })
                    @Html.ValidationMessageFor(m => m.Password, "")
                </div>