Как получить идентификатор свойства модели для использования с настраиваемым IClientValidatable в MVC3 - PullRequest
3 голосов
/ 05 августа 2011

Я пытаюсь написать пользовательский атрибут проверки, который потребует обязательного заполнения полей на основе логических свойств модели.

У меня есть атрибут, реализующий IClientValidatable. У меня есть имя свойства для проверки, но я не знаю, как получить идентификатор клиента целевого свойства.

public IEnumerable<ModelClientValidationRule> 
                        GetClientValidationRules(ModelMetadata metadata, 
                                                 ControllerContext context)
{
    var clientTarget = ?????;
    var rule = new ModelClientValidationRule()
    {
        ErrorMessage = 
            FormatErrorMessage(metadata.DisplayName ?? metadata.PropertyName),
        ValidationType = "requiredif"
    };

    rule.ValidationParameters["target"] = clientTarget;

    yield return rule;
}

JavaScript:

$.validator.addMethod("requiredif", function (value, element, target)
{
    //check on value of target
});

$.validator.unobtrusive.adapters.addSingleVal("requiredif", "target");

Как я могу получить идентификатор клиента целевого свойства, чтобы javascript на стороне клиента мог проверить значение?

Ответы [ 4 ]

6 голосов
/ 05 ноября 2014

Я взял отличный ответ Натана, добавил несколько комментариев и обернул его в метод расширения с именем GetHtmlId, чтобы теперь я мог использовать такой код для получения идентификатора HTML любого другого элемента на той же странице:

    public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule {
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
            ValidationType = "requiredif",
        };

        // Find the value on the control we depend on...
        string depProp = this.GetHtmlId(metadata, context, this.DependentPropertyName);

        rule.ValidationParameters.Add("dependentproperty", depProp);

        yield return rule;
    }

А вот метод расширения:

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

namespace sbs.Lib.Web.ValidationAttributes
{
    public static class IClientValidatableExtensions
    {
        /// <summary> Returns the HTML ID of the specified view model property. </summary>
        /// <remarks> Based on: http://stackoverflow.com/a/21018963/1637105 </remarks>
        /// <param name="metadata"> The model metadata. </param>
        /// <param name="viewContext"> The view context. </param>
        /// <param name="propertyName"> The name of the view model property whose HTML ID is to be returned. </param>
        public static string GetHtmlId(this IClientValidatable me,
                                       ModelMetadata metadata, ControllerContext context,
                                       string propertyName)
        {
            var viewContext = context as ViewContext;

            if (viewContext == null || viewContext.ViewData.TemplateInfo.HtmlFieldPrefix == string.Empty) {
                return propertyName;
            } else {
                // This is tricky.  The "Field ID" returned by GetFullHtmlFieldId is the HTML ID
                // attribute created by the MVC view engine for the property whose validator is
                // being set up by the caller of this routine. This code removes the property
                // name from the Field ID, then inserts the specified property name.
                // Of course, this only works for elements on the same page as the caller of
                // this routine!
                string fieldId = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("");
                fieldId = fieldId.Remove(fieldId.LastIndexOf("_"));
                return fieldId + "_" + propertyName;
            }
        }
    }
}
2 голосов
/ 09 января 2014

Это было проверено только с MVC5, но я подозреваю, что оно не изменилось с MVC3.

Это довольно уродливо, но, похоже, работает.Есть два предположения:

  1. MVC Framework фактически всегда передает ViewContext для аргумента ControllerContext GetClientValidationRules(....).Во всех моих тестах это имело место, но я не могу гарантировать это на 100%.
  2. Другое свойство находится на том же уровне класса модели (например, не является подчиненным свойством сложного типа)

Если оба эти предположения верны, то, похоже, сработает следующее:

 var viewContext = (ViewContext)context;
 if(ViewData.TemplateInfo.HtmlFieldPrefix != string.Empty)
 {
      string fieldId = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("");
      fieldId = fieldId.Remove(fieldId.LastIndexOf("_"));
      fieldId = fieldId + "_" + BooleanPropertyName
 }
 else
 {
      string fieldId = BooleanPropertyName
 }
1 голос
/ 05 февраля 2014

Это работает для меня, возможно, потребуется немного доработать, но вы можете понять:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    var rule = new ModelClientValidationRule()
    {
        ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
        ValidationType = "requiredif",
    };

    string depProp = BuildDependentPropertyId(metadata, context as ViewContext);

    // find the value on the control we depend on;
    // if it's a bool, format it javascript style 
    // (the default is True or False!)
    string targetValue = (this.TargetValue ?? "").ToString();
    if (this.TargetValue != null && this.TargetValue.GetType() == typeof(bool))
        targetValue = targetValue.ToLower();

    rule.ValidationParameters.Add("dependentproperty", depProp);
    rule.ValidationParameters.Add("targetvalue", targetValue);

    yield return rule;
}

private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
    return QualifyFieldId(metadata, this.DependentProperty, viewContext);
}

Атрибут свойства как:

[RequiredIf("SelectedPeriod", "DateRange", ErrorMessageResourceName = "FromDateRequired", ErrorMessageResourceType = typeof(Common))]
public DateTime? StartDate { get; set; }
//dependent property
public string SelectedPeriod { get; set; }

А вот как получить идентификатор поля:

protected string QualifyFieldId(ModelMetadata metadata, string fieldId, ViewContext viewContext)
{
    // build the ID of the property
    string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(fieldId);

    var thisField = metadata.PropertyName + "_";
    if (depProp.StartsWith(thisField))
    // strip it off again
    depProp = depProp.Substring(thisField.Length);
else if (null != metadata.ContainerType && !string.IsNullOrEmpty(metadata.ContainerType.Name))
    {
        depProp = metadata.ContainerType.Name + "_" + fieldId;
    }
    return depProp;
}
0 голосов
/ 05 августа 2011

Взгляните на эту статью . он обсуждает проверку DataAnnotation на стороне сервера, а также демонстрирует, как перехватить эти атрибуты на стороне клиента путем реализации IClientVaildatable и написания некоторого jquery на стороне клиента.

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