I have this strongly typed view form for my model. Problem is that I can't seem to get the IDs right. My view model has form models within it so I have to call some elements like:
<%: Html.TextBoxFor(m => m.Business.Business2)%>
Problem is with this code:
viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(partialFieldName)
If my partialFieldName is "Business2" it should return "Business_Business2" but it only returns "Business2". However, if I use
<%: Html.EditorFor(x => x.Business) %>
it returns "Business_Business2". I can't use Html.EditorFor since I have css classes to place on my form elements. I can't do much of my client side validations without the right Id so this has really been bugging me. Any ideas?
Here are my codes:
Models
public class BusinessModel
{
public string Business1 { get; set; }
public string Business2 { get; set; }
}
public class AccountModel
{
public string Account1 { get; set; }
[MustMatch("Account1")]
public string Account2 { get; set; }
}
public class SampleWrappedModel
{
public BusinessModel Business { get; set; }
public AccountModel Account { get; set; }
}
Validations
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class MustMatchAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "Must match {0}";
public MustMatchAttribute(string propertyToMatch)
: base(DefaultErrorMessage)
{
PropertyToMatch = propertyToMatch;
}
public string PropertyToMatch { get; private set; }
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, PropertyToMatch);
}
public override bool IsValid(object value)
{
throw new Exception("MustMatchAttribute requires the DataAnnotationsMustMatchValidator adapter to be registered");
}
}
//Adapter class
public class DataAnnotationsMustMatchValidator : DataAnnotationsModelValidator<MustMatchAttribute>
{
public DataAnnotationsMustMatchValidator(ModelMetadata metadata, ControllerContext context, MustMatchAttribute attribute)
: base(metadata, context, attribute)
{
}
public override System.Collections.Generic.IEnumerable<ModelValidationResult> Validate(object container)
{
var propertyToMatch = Metadata.ContainerType.GetProperty(Attribute.PropertyToMatch);
if (propertyToMatch != null)
{
开发者_StackOverflow中文版 var valueToMatch = propertyToMatch.GetValue(container, null);
var value = Metadata.Model;
bool valid = Equals(value, valueToMatch);
if (!valid)
{
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
public override System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
string propertyIdToMatch = GetFullHtmlFieldId(Attribute.PropertyToMatch);
yield return new ModelClientMustMatchValidationRule(ErrorMessage, propertyIdToMatch);
}
private string GetFullHtmlFieldId(string partialFieldName)
{
ViewContext viewContext = (ViewContext)ControllerContext;
return viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(partialFieldName);
}
}
public class ModelClientMustMatchValidationRule : ModelClientValidationRule
{
public ModelClientMustMatchValidationRule(string errorMessage, string propertyIdToMatch)
{
ErrorMessage = errorMessage;
ValidationType = "mustMatch";
ValidationParameters.Add("propertyIdToMatch", propertyIdToMatch);
}
}
What the TemplateInfo does with GetFullHtmlFieldName and GetFullHtmlFieldId methods, is simply prepending HtmlFieldPrefix to passed partialFieldName. If I understood correctly, you need kind of helper able to return html field name from labmda expression
public static string GetHtmlFieldName<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
return ExpressionHelper.GetExpressionText(expression);
}
And then you could use
Html.GetHtmlFieldName(m => m.Business.Business2)
This will return Business.Business2
I can't use Html.EditorFor since I have css classes to place on my form elements.
You could write a custom editor template then. So in your view:
<%: Html.EditorFor(x => x.Business) %>
and then inside ~/Views/Shared/EditorTemplates/BusinessModel.ascx
:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BusinessModel>" %>
<%: Html.TextBoxFor(m => m.Business1) %>
<%: Html.TextBoxFor(m => m.Business2) %>
...
or if you don't like the conventional places and names for templates you could customize that as well:
<%: Html.EditorFor(x => x.Business, "MyBusinessTemplate") %>
or if it is only a matter of applying additional attributes to your input elements you could do it in an even more elegant way: by writing a custom DataAnnotationsModelMetadataProvider.
I would recommend you to always use EditorFor
instead of TextBoxFor
.
精彩评论