0

I need to add a prefix to the name of form elements that are rendered within a form. I've created a custom attribute to decorate a property that accepts the name of another property whose value will be used for the name prefix.

public class HtmlElementNamePrefixPropertyAttribute : Attribute {

    public string PropertyName { get; set; }

    public HtmlElementNamePrefixPropertyAttribute(string propertyName) {
        PropertyName = propertyName;
    }
}

And my custom ModelMetadataProvider:

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

        HtmlElementNamePrefixPropertyAttribute nameAttribute = attributes.OfType<HtmlElementNamePrefixPropertyAttribute>().FirstOrDefault();
        if (nameAttribute != null) {
            ModelMetadata prefixMetadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor, metadata.ContainerType, nameAttribute.PropertyName);
            metadata.PropertyName = string.Format("{0}{1}", prefixMetadata.Model, metadata.PropertyName);
        }

        return metadata;
    }
}

As an example, if I decorate Address1 with HtmlElementNamePrefixAttribute:

[HtmlElementNamePrefix("AddressType")]
public string Address1 { get; set; }

public AddressType AddressType { get; set; }

And in my view render a textbox for Address1:

@Html.TextBoxFor(m => m.Address1)

It should render as (assuming that AddressType == AddressType.Home)

<input name="HomeAddress1" value="123 Street way"/>

I have a few problems:

  1. I'm not sure how to effect the rendered name HTML attribute from AddressModelMetadataProvider or what property in ModelMetadata would allow me to do that. My current attempt was to change the PropertyName property of ModelMetadata, but that doesn't have a setter.
  2. If possible, I don't want to create a new HtmlHelper as this attribute could apply to any type of form element that would be rendered in an Address. I also don't want to create a string EditorTemplate since this scenario only applies to an Address object and its properties.

To give a better understanding of what I'm trying to accomplish and why, let me give a brief explanation of the project and its purpose.

Our application allows users to create "fields". From the end users perspective, "fields" can be a single line textbox, multi line textbox (textarea), chooseMany (which is a group of checkboxes), chooseOne (which is a dropdown), address (which consists of more than one form element and can be of type home, business or other), contact (email, phone, and fax), and others. I've simplified a great deal, but I think this gets the point across.

All this information is stored in the database ("field" values, "field" metadata, which "fields" are on the requested "form", etc.), and at runtime used to configure the "form" the user is requesting (i.e., /forms/123). So the form may have a textbox and an address "field", or maybe a home address "field" and a business address "field". Each "form" is created and configured by an administrative user. These "fields" or rather models, inherit from IDataItem and have their own views (templates) that describe how they should be rendered to the UI.

Because of the dynamic nature of a "form" custom model binding and validation was needed (i.e., custom ValidationAttributes, ValidatorProviders, ModelBinders, etc.). Validation rules and logic are applied dynamically at runtime using custom and standard ValidationAttributes (i.e., RequiredAttribute is used for simple "fields" like a single line textbox or chooseOne). This is done at runtime, because the administrator building the "form" can mark a "field" as required or not (as well as other validation constraints).

Required validation for an address "field" is different, because an address is not considered complete unless all parts of the address are filled out (with the exception of address2 and address3).

For client side validation we're using the standard MVC client validation library, jQuery validate. Here in lies my problem... Error messages are applied by jQuery validate based on the name of the form element. So if address1 is invalid, but there is a home address "field" and a business address "field" on the form then a distinction needs to be made between each form element name, so that the error message can be applied to the correct element. This is the biggest reason why I need to prefix the address form element names with AddressType.

This is a very long-winded simplified explanation, but I think it relays the purpose of the application and why my problem exists.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
bflemi3
  • 6,698
  • 20
  • 88
  • 155
  • The `metadata.PropertyName` does not have a setter for a very good reason. The whole purpose of the html helpers is to provide 2 way binding by ensuring the `name` attribute matches your property name and can be bound on post back. I suspect you have fallen into the classic X-Y problem. What reason would you have to add a prefix to a property? –  Feb 07 '15 at 00:29
  • We have a highly dynamic form builder app where a user can choose to render different types of models, address being one of them. Address is unique in that it contains more than one form field and as such, requires unique names in order to validate and save data. – bflemi3 Feb 07 '15 at 05:25
  • I can only assume from your comments that your not understanding what you can do using MVC out of the box. Are you able to give an example of the model and view indicating where the issue is and why you think you need to add a prefix. –  Feb 07 '15 at 05:51
  • I understand MVC very well, I think I'm not doing a good job of explaining my problem. I've added a long short explanation of the project. Hopefully that helps clear some confusion and better relays my need for form element name prefixes. – bflemi3 Feb 07 '15 at 14:52
  • If your rendering the address in a partial, then you could add the prefix using a custom helper - something like `@Html.PartialFor(m => m.Address1, "YourPartalName", "YourPrefix")` which perpends "YourPrefix" using `TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = prefix}`. If you want to use a `DataAnnotation` to supply the prefix on the property, your attribute would need to inherit `Attribute` and implement `IMetadataAware` to add the prefix to the metadata `AdditionalValues` which could be read by the helper. Happy to give you an example if that meets your needs. –  Feb 08 '15 at 00:22
  • An example would be much appreciated :) Thank you Stephen! – bflemi3 Feb 08 '15 at 15:48
  • 1
    The examples [here](http://stackoverflow.com/questions/27612442/how-to-persist-data-models-passed-to-partial-views/27612476#27612476) and [here](http://stackoverflow.com/questions/27967080/reusable-checkbox-partial-view/27976041#27976041) show how to append a prefix to a partial (in you case the custom helper would need to be modified to include a parameter for the 'prefix') –  Feb 08 '15 at 22:39
  • 1
    And [this answer](http://stackoverflow.com/questions/26519493/customattribute-reflects-html-attribute-mvc5/26519946#26519946) gives an example of creating a custom attribute that can be read by a custom helper. –  Feb 08 '15 at 22:46
  • You should post this as your answer so I can give you credit. Thanks for your help – bflemi3 Feb 09 '15 at 16:22
  • In this case if you have made it work, you should post your own answer with your code, and accept it. In any case I have a feeling there may be a better, or at least more flexible solution - just not sure what it is yet. And if the linked answers helped, you can always vote on them :) –  Feb 10 '15 at 02:31

0 Answers0