The code I am looking to produce is similar to:
<div class="form-group">
<label for="email">Email address</label>
<input type="email" class="form-control" id="email" placeholder="Enter email">
</div>
Using something like this:
@Html.LabelledTextBoxFor(model => model.EmailAddress)
Where the label text get derived from model.EmailAddress
This would be done using Data Annotation e.g.
[Displayname("Email Address]
What is the best way to accomplish this?
Will this affect the auto generated client side JS validation using jquery val?
I wrote an extension method, maybe this might help:
public static MvcHtmlString LabelledTextBoxFor<TModel, TResult>(this HtmlHelper<TModel> html, Expression<Func<TModel, TResult>> expression)
{
ExpressionType type = expression.Body.NodeType;
if (type == ExpressionType.MemberAccess)
{
MemberExpression memberExpression = (MemberExpression) expression.Body;
var propName = memberExpression.Member.Name;
var member = memberExpression.Member as PropertyInfo;
var attributes = member.GetCustomAttributes();
StringBuilder sb = new StringBuilder();
foreach (var attribute in attributes)
{
if (attribute is DisplayAttribute)
{
DisplayAttribute d = attribute as DisplayAttribute;
var displayName = d.Name;
sb.Append("<div class=\"form-group\">");
sb.AppendFormat("<label for=\"{0}\">{1}</label>", propName, displayName);
sb.AppendFormat(
"<input type=\"email\" class=\"form-control\" id=\"{0}\" placeholder=\"Enter email\">",
propName);
sb.Append("</div>");
return MvcHtmlString.Create(sb.ToString());
}
}
}
return MvcHtmlString.Create("");
}
You can use default display attribute to specify display name.There is no need for custom attributes.And you can use this extension like this:
@Html.LabelledTextBoxFor(model => model.EmailAddress)
Note: I have tried myself and it is working correctly.
Update: More simple version
public static MvcHtmlString LabelledTextBoxFor2<TModel, TResult>(this HtmlHelper<TModel> html, Expression<Func<TModel, TResult>> expression)
{
ExpressionType type = expression.Body.NodeType;
if (type == ExpressionType.MemberAccess)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var displayName = metadata.DisplayName;
var propName = metadata.PropertyName;
StringBuilder sb = new StringBuilder();
sb.Append("<div class=\"form-group\">");
sb.AppendFormat("<label for=\"{0}\">{1}</label>", propName, displayName);
sb.AppendFormat(
"<input type=\"email\" class=\"form-control\" id=\"{0}\" placeholder=\"Enter email\">",
propName);
sb.Append("</div>");
return MvcHtmlString.Create(sb.ToString());
}
return MvcHtmlString.Create("");
}
I would use @Html.EditorFor template. So create EditorTemplates folder in Views/Shared in your MVC project with a name EmailAddress.cshtml - it will display this template for every [DataType(DataType.EmailAddress)] defined in your model.
So: EmailAddress.cshtml should look sth like this:
<div class="form-group">
<label for="email">@Html.LabelFor(x => x)</label>
@Html.TextBoxFor(x => x, new { @class = "form-control", placeholder = ViewData.ModelMetadata.Watermark })
</div>
Model should look like this:
public class SomeModel {
[DataType(DataType.EmailAddress)]
[Display(Name="Some string for LabelFor", Prompt = "Placeholder in input"]
public string SomeEmail { get; set; }
}
Note here, that I've used Prompt property of Display metadata, which I linked to placeholder via ViewData.ModelMetadata.Watermark in EmailAddress.cshtml.
Html.LabelFor will always take property Name from Display and DataType is needed for linking between EditorTemplates and model property.
And invoke all this "magic" by using
@Html.EditorFor(m=>m.SomeEmail)
on a page, where you use SomeMode model.
I hope this does make sense.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With