Поведение пользовательских сообщений проверки ASP.NET MVC3 - PullRequest
2 голосов
/ 23 января 2012

Исходя из модели веб-форм asp.net, я привык использовать валидаторы, которые отображают ошибку в виде <span title="Username is Required">*</span>.

Я вполне понимаю, как валидаторы MVC3 работают «из коробки», поэтому, пожалуйста, не нужно больше ответов, объясняющих, как валидаторы работают в MVC3, так как я уверен, что у меня это получилось. Я пытаюсь выполнить сообщение об ошибке валидации в качестве заголовка тега span, как показано в первом абзаце.

Мне удалось воспроизвести это в MVC3, но я не уверен, что способ, которым я это сделал, следует передовой практике. Я был бы признателен за любую информацию о том, есть ли лучший способ сделать то же самое. Было бы здорово, если бы это можно было сделать без изменения jquery.validate.unobtrusive.js.

Итак, что я сделал:

  1. Установить сообщение проверки на "*"
  2. Скрытое сообщение проверки, пока оно действительно
  3. Добавлен новый атрибут, чтобы определить, добавлять ли сообщение в качестве заголовка
  4. Добавлены 2 строки отмеченного кода в onError, чтобы проверить, отображать ли сообщение об ошибке в заголовке и, если да, то сделать это.
    [.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 ]

3 голосов
/ 04 июня 2012

Я думаю, что вы сделали, это самый чистый способ. Нет никакого способа изменить 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));
    }
}
0 голосов
/ 23 января 2012

Все опубликованные вами 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>
...