I have code like this that I repeat through many MVC editing views. This example is the default way we display a checkbox, but similar repetition is found with other input types.
<div class="form-group">
@Html.LabelFor(model => model.IsLive, htmlAttributes: new { @class = "control-label col-md-3" })
<div class="col-md-8 checkbox">
<div class="col-xs-1">
@Html.EditorFor(model => model.IsLive)
</div>
<div class="col-xs-10">
@Html.CheckboxLabelFor(model => model.IsLive)
</div>
</div>
<a class="infoonclick col-md-1" title="@Html.DisplayNameFor(model => model.IsLive)" data-content="@Html.DescriptionFor(model => model.IsLive)">
<span class="fa fa-info-circle"></span>
</a>
</div>
I am wondering what is the best way to DRY and standardise this?
I want to do something like @Html.DefaultCheckboxEditorFor(model => model.IsLive)
I tried creating a custom HtmlHelper, but this seemed to involve too many hard coded strings to be a good idea.
Rather I feel I should be using EditorTemplates for this, but I can't quite get the syntax right. The model for the view is a bool, but I need to get property specific stuff like the display name and descriptions.
@model bool
<div class="form-group">
@Html.LabelFor(model => model.IsLive, htmlAttributes: new { @class = "control-label col-md-3" })
<div class="col-md-8 checkbox">
<div class="col-xs-1">
@Html.EditorFor(model => model.IsLive)
</div>
<div class="col-xs-10">
@Html.CheckboxLabelFor(model => model.IsLive)
</div>
</div>
<a class="infoonclick col-md-1" title="@Html.DisplayNameFor(model => model.IsLive)" data-content="@Html.DescriptionFor(model => model.IsLive)">
<span class="fa fa-info-circle"></span>
</a>
</div>
I have a project where most of my views look like: (This also works with multi-level deep complex objects, but not with any type of collection, like IEnumerable, although it could be modified to do so)
<h3>Edit existing page</h3>
<div class="col-xs-12">
@using (Html.BeginForm("Edit", "Page", FormMethod.Post, new { role = "role" }))
{
@Html.EditorForModel()
<input type="submit" value="Save" class="btn btn-primary" />
}
</div>
I think that's pretty cool. So the model looks like:
public class PageEditViewModel
{
[Editable(false)]
[DisplayName("Page Id")]
public Guid Id { get; set; }
[Editable(false)]
[DisplayName("Url to resource (format: '/my-resource' or '/sub/resource)'")]
public string Url { get; set; }
[Required]
[MaxLength(50, ErrorMessage = "Maximum Length of 50 Exceeded.")]
[DisplayName("Title for page (must match Url ex: 'My Resource' or 'Sub Resource'")]
public string PageTitle { get; set; }
[MaxLength(int.MaxValue, ErrorMessage = "Content Exceeded Maximum Length")]
[DataType(DataType.MultilineText)]
public string Content { get; set; }
}
I have some editor templates:
\Views\Shared\EditorTemplates\multilinetext.cshtml
@model object
@{
var htmlAttributes = this.ViewData.ModelMetadata.GetHtmlAttributes();
}
<div class="form-group @Html.ErrorClassFor(m => m, "has-error")">
@Html.LabelFor(m => m, new { @class = "control-label" })
<div class="controls">
@Html.TextAreaFor(
m => m,
8, 8,
htmlAttributes)
@Html.ValidationMessageFor(m => m, null, new { @class = "help-block" })
</div>
</div>
And it all magically works with the a modified version of object.cshtml:
@model object
@using System.Text;
@using System.Data;
@{
ViewDataDictionary viewData = Html.ViewContext.ViewData;
TemplateInfo templateInfo = viewData.TemplateInfo;
ModelMetadata modelMetadata = viewData.ModelMetadata;
System.Text.StringBuilder builder = new StringBuilder();
string result;
// DDB #224751
if (templateInfo.TemplateDepth > 2)
{
result = modelMetadata.Model == null ? modelMetadata.NullDisplayText
: modelMetadata.SimpleDisplayText;
}
foreach (var prop in modelMetadata.Properties.Where(pm =>
pm.ShowForEdit
//&& pm.ModelType != typeof(System.Data.EntityState)
&& !templateInfo.Visited(pm)
)
.OrderBy(pm => pm.Order))
{
//Type modelType = Model.GetType();
Type modelType = modelMetadata.ModelType;
System.Reflection.PropertyInfo pi = modelType.GetProperty(prop.PropertyName);
System.ComponentModel.DataAnnotations.DisplayAttribute attribute = pi.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute), false).FirstOrDefault() as System.ComponentModel.DataAnnotations.DisplayAttribute;
if (attribute != null
&& !string.IsNullOrWhiteSpace(attribute.GetGroupName()))
{
//builder.Append(string.Format("<div>{0}</div>", attribute.GetGroupName()));
builder.Append(Html.Partial("Partial-GroupName", attribute.GetGroupName()));
}
builder.Append(Html.Editor(prop.PropertyName, prop.TemplateHint ?? prop.DataTypeName).ToHtmlString());
}
result = builder.ToString();
}
@Html.Raw(result)
Example output:
My EditFor templates are versions of MacawNL BootstrapEditorTemplates (which I have no affiliation with).