3

Я пытаюсь создать пользовательский ModelMetadataProvider, чтобы предоставить ненавязчивые атрибуты виджета автозаполнения пользовательского интерфейса JQuery.ModelMetadata TemplateHint always null

У меня есть пользовательский атрибут, который выглядит следующим образом:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
public sealed class AutocompleteAttribute : Attribute, IMetadataAware 
{ 
    public void OnMetadataCreated(ModelMetadata metadata) 
    { 
     metadata.TemplateHint = "Autocomplete"; 
    } 
} 

и шаблон редактора, который выглядит следующим образом:

@{ 
    var attributes = new RouteValueDictionary 
    { 
     {"class", "text-box single-line"}, 
     {"autocomplete", "off"}, 
     {"data-autocomplete-url", "UrlPlaceholder" }, 
    }; 
} 

@Html.TextBox("", ViewContext.ViewData.TemplateInfo.FormattedModelValue, attributes) 

У меня есть ViewModel со свойством типа string, которая включает AutocompleteAttribute:

public class MyViewModel 
{ 
    [Autocomplete] 
    public string MyProperty { get; set; } 
} 

Когда я использую is viewModel в моем представлении Я проверяю сгенерированный html, и я получаю тег <input>, который имеет такой атрибут: data-autocomplete-url="UrlPlaceholder".

То, что я хочу делать дальше, чтобы иметь возможность указать URL, на мой взгляд, который использует мои ViewModel как это:

@model MyViewModel 

@{ ViewBag.Title = "Create item"; } 

@Html.AutoCompleteUrlFor(p => p.MyProperty, UrlHelper.GenerateUrl(null, "Autocomplete", "Home", null, Html.RouteCollection, Html.ViewContext.RequestContext, true)) 

// Other stuff here... 

<div> 
    @Html.ActionLink("Back to List", "Index") 
</div> 

Мой AutoCompleteForUrl помощник просто сохраняет сгенерированный URL в словаре, используя свойство имя в качестве ключа.

Далее Я создал обычай ModelMetadataProvider и зарегистрировал его в global.asax, используя эту строку кода ModelMetadataProviders.Current = new CustomModelMetadataProvider();.

Что я хочу сделать, это вставить URL-адрес, который будет использоваться виджетами автозаполнения JQuery UI, в словарь metadata.AdditionalValues, который будет использоваться шаблоном редактора Autocomplete.

Моего заказ ModelMetadataProvider выглядит следующим образом:

public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider 
{ 
    protected override ModelMetadata CreateMetadata(IEnumerable<System.Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) 
    { 
     var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); 

     if (metadata.TemplateHint == "Autocomplete") 
     { 
      string url; 
      if(htmlHelpers.AutocompleteUrls.TryGetValue(metadata.propertyName, out url) 
      { 
       metadata.AdditionalValues["AutocompleteUrl"] = url; 
      } 
     } 

     return metadata; 
    } 
} 

и мой обновленный шаблон редактора выглядит следующим образом:

@{ 
    object url; 

    if (!ViewContext.ViewData.ModelMetadata.TryGetValue("AutocompleteUrl", out url)) 
    { 
     url = ""; 
    } 

    var attributes = new RouteValueDictionary 
    { 
     {"class", "text-box single-line"}, 
     {"autocomplete", "off"}, 
     {"data-autocomplete-url", (string)url }, 
    }; 
} 

@Html.TextBox("", ViewContext.ViewData.TemplateInfo.FormattedModelValue, attributes) 

Проблема есть, TemplateHint свойства никогда не равно «подсказки» в моей пользовательской модели поставщик метаданных, поэтому моя логика генерации URL-адреса никогда не вызывается. Я бы подумал, что в этот момент свойство TemplateHint будет установлено так, как я назвал базовую реализацию CreateMetadataDataAnnotationsModelMetadataProvider.

Вот что я могу подтвердить:

  • CustomModelMetadataProvider правильно зарегистрирован, поскольку он содержит другой код, который вызывался.
  • Правильный шаблон редактора получает выбранный HTML-код, содержащий атрибут "data-autocomplete-url".
  • Если я поставил точку останова в шаблоне автозаполнения, Visual Studio отправится к отладчику.

Так может ли кто-нибудь пролить свет на это для меня, пожалуйста? Что я не понимаю о системе ModelMetadataProvider?

ответ

3

Просмотрев ASP.NET MVC 3 Исходный код Я обнаружил, что причина этого в том, что метод CreateMetadata вызывается до метода OnMetadataCreated любых атрибутов IMetadataAware, которые применяются к модели.

Я нашел альтернативное решение, которое позволяет мне делать то, что я хотел.

Прежде всего я обновил мой AutocompleteAttribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
public class AutocompleteAttribute : Attribute, IMetadataAware 
{ 
    public const string Key = "autocomplete-url"; 
    internal static IDictionary<string, string> Urls { get; private set; } 

    static AutocompleteAttribute() 
    { 
     Urls = new Dictionary<string, string>(); 
    } 

    public void OnMetadataCreated(ModelMetadata metadata) 
    { 
     metadata.TemplateHint = "Autocomplete"; 

     string url; 

     if (Urls.TryGetValue(metadata.PropertyName, out url)) 
     { 
      metadata.AdditionalValues[Key] = url; 
      Urls.Remove(metadata.PropertyName); 
     } 
    } 
} 

и мой Html вспомогательный метод для настройки URL в моих взглядах выглядит следующим образом:

public static IHtmlString AutocompleteUrlFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string url) 
{ 
    if (string.IsNullOrEmpty(url)) 
     throw new ArgumentException("url"); 

    var property = ModelMetadata.FromLambdaExpression(expression, html.ViewData).PropertyName; 
    AutocompleteAttribute.Urls[property] = url; 

    return MvcHtmlString.Empty; 
} 

И тогда все, что нужно сделать в мой шаблон редактора таков:

@{ 
    object url; 
    ViewData.ModelMetadata.AdditionalValues.TryGetValue(AutocompleteAttribute.Key, out url); 

    var attributes = new RouteValueDictionary 
    { 
     {"class", "text-box single-line"}, 
     {"autocomplete", "off"}, 
     { "data-autocomplete-url", url }, 
    }; 
} 

@Html.TextBox("", ViewContext.ViewData.TemplateInfo.FormattedModelValue, attributes)