2

Hoping to find a way when in MVC5 a Custom attribute or preferable the RegularExpressionAttribute decorates a property in the model the html control will contain it as another attribute of the control. E.g.

class CoolModel {
   [CustomHtmlAttribute("hello")]
   public string CoolValue {get;set;} 
}

outputs...

<input type="text" customhtml="hello" />

Or something like that. So for the RegularExpressionAttribute the pattern attribute will be awesome.

class CoolModel {
   [RegularExpressionAttribute("/d")]
   public string CoolValue {get;set;} 
}

outputs...

<input type="text" pattern="/d" />

I need this output without enabling the Javascript unobtrusive option. So I'm thinking in a way to specify some attribute in the model that gets push down to the view. Not sure if the Data annotations provider could do this job. Not sure if a Helper could be extended to get this result.

Help is appreciated.

user1791567
  • 1,388
  • 1
  • 16
  • 29
  • 1
    To achieve this you can just use the standard helpers and add html attributes `@Html.TextBoxFor(m => m.SomeProperty, new { customhtml = "hello" })`. Doing it using custom data annotations will be much more complex –  Oct 23 '14 at 00:54
  • See [this link](http://aspadvice.com/blogs/kiran/archive/2009/11/29/Adding-html-attributes-support-for-Templates-_2D00_-ASP.Net-MVC-2.0-Beta_2D00_1.aspx) – artm Oct 23 '14 at 00:59
  • Thanks both for the answers. @StephenMuecke I'm aware of that but I need to specify the attribute in the model for maintainability purposes. – user1791567 Oct 23 '14 at 01:01
  • @artm I checked that link earlier and I wonder if there is another way to do it. Could you add a meta attribute that gets to be render in the view without doing that whole process? – user1791567 Oct 23 '14 at 01:03
  • 1
    In that case artm's link may be useful, although its a bit out of date and does rely on creating custom `EditorTemplates` which can be difficult to maintain across projects. The other alternative is to implement IMetadataAware to add the attributes to `metadata.AdditionalValues`, then create a set of custom html helpers that merge the attributes –  Oct 23 '14 at 01:07
  • You need to implement the whole thing if you want to add custom attributes to be displayed in views. Easier way is @StephenMuecke 's response. Depends how often you're going to use the attribute. If you need it on 50 model properties implement the attribute once, if you need it on 5 properties, add it inside the view like Stephen suggested. – artm Oct 23 '14 at 01:07
  • @StephenMuecke - Yes, I discarded that solution earlier. Could you share an example of your suggestion please? – user1791567 Oct 23 '14 at 01:08
  • @StephenMuecke That's true, IMetadataAware is a better way of doing it, link is bit old. Plenty of IMetadataAware examples on the internet. – artm Oct 23 '14 at 01:08
  • @artm - Yes, we will use this approach in an extensive way. Big big forms.. :( – user1791567 Oct 23 '14 at 01:10
  • I'll post a simple example shortly –  Oct 23 '14 at 01:13
  • @StephenMuecke - Can I add metadata.AdditionalValues in the Data annotations provider? I'm saying this because I already have this mechanism implemented. However the question remains.. By adding this, will the new attributes be reflected when the view is rendered or I need to write a custom html helper anyways? – user1791567 Oct 23 '14 at 01:19
  • Adding the values to `AdditionalValues` is not enough. You will need to use custom helpers or use custom `EditorTemplates` in order to render the values in the view –  Oct 23 '14 at 01:21
  • That's what I thought... – user1791567 Oct 23 '14 at 01:24

1 Answers1

4

If using the standard helpers with the overload to add html attributes is not acceptable, then you can create an attribute implements IMetadataAware that adds properties to metadata.AdditionalValues which can then be used in custom html helpers. A simple example might be

[AttributeUsage(AttributeTargets.Property)]
public class CustomHtmlAttribute : Attribute, IMetadataAware
{
  public static string ValueKey
  {
    get { return "Value"; }
  }
  public string Value { get; set; }
  public void OnMetadataCreated(ModelMetadata metadata)
  {
    if (Value != null)
    {
      metadata.AdditionalValues[ValueKey] = Value;
    }
  }
}

and to create a helper to render a textbox (only one overload shown here)

public static MvcHtmlString CustomHtmlTextBoxFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
{
  ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
  object attributes = null;
  if (metaData.AdditionalValues.ContainsKey(ValueKey))
  {
    attributes = new { customhtml = (string)metaData.AdditionalValues[ValueKey] };
  }
  return InputExtensions.TextBoxFor(helper, expression, attributes);
}

and use it as

[CustomHtml(Value = "hello")]
public string CoolValue { get; set; } 

and in the view

@Html.CustomHtmlTextBoxFor(m => m.CoolValue)

to make this a bit more flexible, you could add more properties to the attribute so you could apply it as

[CustomHtml(Value = "hello", Pattern="/d")]
public string CoolValue { get; set; }

and modify the helper to render all the html attributes you define.