Использование собственного валидатора в списке переменной длины в Microsoft MVC 2 (проблемы валидации на стороне клиента) - PullRequest
1 голос
/ 30 августа 2010

Я создал список переменной длины в соответствии со многими замечательными постами Стива Сандерсона о том, как это сделать в MVC 2. В его блоге есть много замечательных уроков.

Затем я создал специальный «requiredif«условный валидатор, следующий за этим обзором http://blogs.msdn.com/b/simonince/archive/2010/06/11/adding-client-side-script-to-an-mvc-conditional-validator.aspx

Я использовал обработчик валидации JQuery из записи блога MSDN, который добавляет следующее в условный валидатор.js, который я включаю в скрипты моей страницы:

(function ($) {
    $.validator.addMethod('requiredif', function (value, element, parameters) {
        var id = '#' + parameters['dependentProperty'];

        // Get the target value (as a string, as that's what actual value will be)
        var targetvalue = parameters['targetValue'];
        targetvalue = (targetvalue == null ? '' : targetvalue).toString().toLowerCase();

        // Get the actual value of the target control
        var actualvalue = ($(id).val() == null ? '' : $(id).val()).toLowerCase();

        // If the condition is true, reuse the existing required field validator functionality
        if (targetvalue === actualvalue)
            return $.validator.methods.required.call(this, value, element, parameters);

        return true;
    });
})(jQuery);

Увы, это не приводит к срабатыванию проверки на стороне клиента ... запускается только проверка на стороне сервера.Присущие «требуемые» валидаторы действительно запускают на стороне клиента, что означает, что мой скрипт правильно настроен на базовую валидацию.Кто-нибудь выполнил пользовательские валидаторы в списке переменной длины в MVC 2, используя JQuery в качестве метода проверки на стороне клиента?

Обратите внимание, что этот же пользовательский валидатор работает на стороне клиента, используя точно такую ​​же настройку насписок переменной длины.

1 Ответ

2 голосов
/ 02 сентября 2010

Оказывается, это была проблема с именованием идентификаторов полей при отображении идентификаторов коллекций в списке переменной длины. Валидатор пытался присвоить ID элемента зависимого свойства ожидаемый оператор:

string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(Attribute.DependentProperty);

Я проанализировал источник просмотра HTML (опубликованный в моем комментарии выше), и фактически символы [и] не выводятся в HTML элементов индекса коллекции ... они заменяются на _... так, когда я изменил свой CustomValidator.cs, чтобы установить для зависимого свойства значение:

string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(Attribute.DependentProperty).Replace("[", "_").Replace("]", "_");

... тогда валидатор на стороне клиента работает, так как имя совпадает. Мне придется копнуть глубже, чтобы понять, ПОЧЕМУ идентификатор переименовывается в методе индекса коллекции Сандерсона, ниже ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Sendz.WebUI.Helpers
{
    public static class HtmlPrefixScopeExtensions
    {
        private const string IdsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";

    public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
    {
        var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
        var itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();

        // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
        html.ViewContext.Writer.WriteLine(
            string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />",
                          collectionName, html.Encode(itemIndex)));

        return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
    }

    public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
    {
        return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
    }

    private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
    {
        // We need to use the same sequence of IDs following a server-side validation failure,  
        // otherwise the framework won't render the validation error messages next to each item.
        var key = IdsToReuseKey + collectionName;
        var queue = (Queue<string>)httpContext.Items[key];
        if (queue == null)
        {
            httpContext.Items[key] = queue = new Queue<string>();
            var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
            if (!string.IsNullOrEmpty(previouslyUsedIds))
                foreach (var previouslyUsedId in previouslyUsedIds.Split(','))
                    queue.Enqueue(previouslyUsedId);
        }
        return queue;
    }

    #region Nested type: HtmlFieldPrefixScope

    private class HtmlFieldPrefixScope : IDisposable
    {
        private readonly string _previousHtmlFieldPrefix;
        private readonly TemplateInfo _templateInfo;

        public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
        {
            _templateInfo = templateInfo;

            _previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
            templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
        }

        #region IDisposable Members

        public void Dispose()
        {
            _templateInfo.HtmlFieldPrefix = _previousHtmlFieldPrefix;
        }

        #endregion
    }

    #endregion
}

}

Полная ссылка на валидатор / атрибут ...

public class RequiredIfAttribute : ValidationAttribute
{
    private RequiredAttribute innerAttribute = new RequiredAttribute();
    public string DependentProperty { get; set; }
    public object TargetValue { get; set; }

    public RequiredIfAttribute(string dependentProperty, object targetValue)
    {
        this.DependentProperty = dependentProperty;
        this.TargetValue = targetValue;
    }

    public override bool IsValid(object value)
    {
        return innerAttribute.IsValid(value);
    }
}

    public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
        : base(metadata, context, attribute) { }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        // Get a reference to the property this validation depends upon
        var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);

        if (field != null)
        {
            // Get the value of the dependent property
            var value = field.GetValue(container, null);

            // Compare the value against the target value
            if ((value == null && Attribute.TargetValue == null) || 
                (value != null && value.ToString().ToLowerInvariant().Equals(Attribute.TargetValue.ToString().ToLowerInvariant())))
            {
                // A match => means we should try validating this field
                if (!Attribute.IsValid(Metadata.Model))
                    // Validation failed - return an error
                    yield return new ModelValidationResult { Message = ErrorMessage };
            }
        }
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        var rule = new ModelClientValidationRule()
        {
            ErrorMessage = ErrorMessage,
            ValidationType = "requiredif"
        };
        var viewContext = (ControllerContext as ViewContext);
        var depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(Attribute.DependentProperty).Replace("[", "_").Replace("]", "_");
        rule.ValidationParameters.Add("dependentProperty", depProp);
        rule.ValidationParameters.Add("targetValue", Attribute.TargetValue.ToString());

        yield return rule;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...