I have a ton of Forms that follow the common format of Label: Control. I decided instead of having pages of the following:
<div class="row-fluid">
<div class="span4">
@Html.LabelFor(m => m.Prop1)
</div>
<div class="span8">
@Html.TextBoxFor(m => m.Prop1)
</div>
</div>
I would create an editor template to hold this pattern, and select the correct default control. Of course, a teammate immediately pointed out that hardcoding the spans was not helpful, so I added a few custom ViewData fields such as "labelSpan".
The real problem comes to this:
@Html.TextBoxFor(m => m, new { @class = @ViewData["class"], style = @ViewData["style"],
id = @ViewData["id"]... etc })
So question 1: How do I consolidate that to pass through directly without having to specify every property?
Question 2: How do I do it in a way such that the following does not conflict?
@Html.LabelFor(m => m, new { @class = @ViewData["LabelClass"], style = @ViewData["LabelStyle"] })
Rather than using ViewData
, you'll want to use ModelMetadata
. If you create a custom ModelMetadataProvider
, you can override the CreateMetadata method to add the appropriate values to the AdditionalAttributes
and then you can later query those values.
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
Type containerType, Func<object> modelAccessor,
Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var labelSpan = "span4"; // do whatever logic you need to.
metadata.AdditionalValues.Add("labelSpan", labelSpan);
...
return metadata;
}
Then you consume the metadata in your editor templates. The pattern that I've found to work is to only put the labels in your Object.cshtml
editor template:
@{
var visibleProperties = ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm)).ToList();
var noHtmlProps = visibleProperties.Where(pm => pm.HideSurroundingHtml).ToList();
var normalProperties = visibleProperties.Where(pm => !pm.HideSurroundingHtml).ToList();
foreach (var prop in normalProperties)
{
<div class="row-fluid">
@Html.Label(prop.PropertyName, prop.DisplayName,
new{@class=ViewData.ModelMetadata.AdditionalValues["labelSpan"]})
@(prop.IsReadOnly
? Html.Display(prop.PropertyName)
: Html.Editor(prop.PropertyName))
@Html.ValidationMessage(prop.PropertyName)
</div>
}
// Output hidden properties at the end
foreach (var prop in noHtmlProps)
{
@Html.Editor(prop.PropertyName)
}
}
Then you use your other types' editor templates to determine which control to use. For example, String.cshtml
might look like this:
@Html.TextBox(string.Empty, ViewData.TemplateInfo.FormattedModelValue,
new{@class = ViewData.ModelMetadata.AdditionalValues["valueSpan"]})