简体   繁体   中英

.NET Custom Strongly Typed HTML Helper with deep property navigation

I've searched all around, read all the posts related to creating a strongly typed helper, but none have addressed my issue. Problem:

When referencing a simple property, all works well:

@Html.TextBoxGroupFor(x => x.BadgeNumber)

When trying to access properties on a different class in my model, I get an error:

@Html.TextBoxGroupFor(x => x.Person.BadgeNumber)

The error is:

The property InspectionEditViewModel.Person.BadgeNumber could not be found.

The problem line is:

var metaData = ModelMetadataProviders.Current.GetMetadataForProperty(() => Activator.CreateInstance<TModel>(), typeof(TModel), displayName);

I'm guessing it has to do with the GetMetadataForProperty not being able to find BadgeNumber on InspectionEditViewModel. MVC's HTML helpers by default are able to do this with no problems.

EDIT:

I knew I left something out, it's been a long day. Here is the Helpers code:

public static MvcHtmlString TextBoxGroupFor<TModel, TProperty>(this HtmlHelper<TModel> helper,
                                                     Expression<Func<TModel, TProperty>> expression)
        {
            var inputName = ExpressionHelper.GetExpressionText(expression);
            var fullHtmlFieldName = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(inputName);
            var metaData = ModelMetadataProviders.Current.GetMetadataForProperty(() => Activator.CreateInstance<TModel>(), typeof(TModel), fullHtmlFieldName);
            var displayName = metaData.DisplayName ?? inputName;
            return TextBoxGroup(helper, displayName, inputName);
        }
ModelMetadata.FromLambdaExpression(propertyExpression, html.ViewData);

This is what you are looking for. Unfortunately, this means that html must be an instance of HtmlHelper from a view whose model is the type you're trying to get the metadata for.

I think that's because it wants to take the property values from the Model/ViewData and pass it by the ModelMetadataProvider to allow it to to populate metadata specific to the model instance in question.

If you don't care about instance-specific metadata (eg: only want data annotation attributes) and so on, then just pass it a new ViewDataDictionary (where TModel is the type whose metadata you want to get).

For bonus feel-good points, the FromLambdaExpression method caches expressions and their resolved property paths inside it to give better performance.

Hope that helps you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM