can i change the way LabelFor render in MVC? - asp.net-mvc

i would like to change the way LabelFor render. Can i do that with a DisplayTemplate?
LabelFor generate a label tag and i would like to add a ":" at the end of the label.
thank you!
alex

Here is an HTML Helper that will do that:
public static class LabelExtensions {
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString SmartLabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) {
return LabelHelper(html,
ModelMetadata.FromLambdaExpression(expression, html.ViewData),
ExpressionHelper.GetExpressionText(expression));
}
internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName) {
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(labelText)) {
return MvcHtmlString.Empty;
}
// uncomment if want * for required field
//if (metadata.IsRequired) labelText = labelText + " *";
labelText = labelText + ":";
TagBuilder tag = new TagBuilder("label");
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
tag.SetInnerText(labelText);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
}
To use it:
<%:Html.SmartLabelFor(m => m.FirstName)%>
It will render:
<label for="FirstName">First Name:</label>
Or if you uncomment the required field related *
<label for="FirstName">First Name *:</label>

Just write a regular <label> element in plain HTML:
<label>My Label:</label>
If you want to output the for="" attribute and accurately render the control's name then use this extension method:
using System;
using System.Linq.Expressions;
using System.Web.Mvc;
namespace MvcLibrary.Extensions
{
public static class HtmlExtensions
{
public static MvcHtmlString FieldIdFor<TModel, TValue>(
this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string inputFieldId = html.ViewContext.ViewData.
TemplateInfo.GetFullHtmlFieldId(htmlFieldName);
return MvcHtmlString.Create(inputFieldId);
}
}
}
Then you can use in your view like so:
<label for="<%= Html.FieldIdFor(m => m.EmailAddress) %>">E-mail address:</label>
<%= Html.TextBoxFor(m => m.EmailAddress) %>
The other posts cover different approaches, they are all equally valid, which one you go for is matter of personal preference. I personally prefer writing the <label> as plain HTML as it gives designers more flexibility with changing markup, adding extra attributes such as CSS classes etc. Also I feel the label text is a view concern and shouldn't be decorated on the ViewModel class, but that's just my personal opinion/preference, I know some people here will disagree with me and that's fine :-)

You can create a String.ascx in DisplayTemplates folder and provide your own implementation. Refer to the Overriding Templates section of the following article.
http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html

You could do this using MVC 2 (if possible) if you pass a custom ViewModel to the view.
using System.ComponentModel;
public class PersonViewModel
{
public PersonViewModel(string name)
{
this.Name = name;
}
[DisplayName(".Display Anything You Like Here.")]
public string Name { get; set; }

I think the best approach would be writing your own helper method that renders what you like. You can overload the existing method or simply create a new method.

Related

How to assign class for all Html.LabelFor

I have an app with about 50 of these:
#Html.LabelFor(m => m.CurrentLineItem.Address.Address1, new { #class = "textBox-label" })
It seems to me hardcoding a class is not a good idea. Is there a way to abstract this out further? Like maybe:
#Html.LabelWithTextBoxLabelClass(m=> m.CurrentLineItem.Address.Address1);
just like others said, make your own Html Helper, code like below:
public static class LabelExtensions
{
public static MvcHtmlString LabelWithTextBoxLabelClass(this HtmlHelper helper, string expression)
{
return helper.Label(expression, new { #class = "textBox-label" });
}
public static MvcHtmlString LabelForWithTextBoxLabelClass<TModel,TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel,TValue>> expression)
{
return helper.LabelFor(expression, new {#class = "textBox-label"});
}
}
now you can use it like what you want:
#Html.LabelWithTextBoxLabelClass(m=> m.CurrentLineItem.Address.Address1);
You can make your own Html helpers, or you can use Bootstrap Html Helpers for MVC. Take a look at here: http://www.codeproject.com/Articles/570762/TwitterBootstrapMvc
With bootstrap you can simply set your label class like this:
#Html.Bootstrap().LabelFor(m => m.Property).Class("textBox-label")

How to customize Html.ValidationMessageFor in ASP MVC

Is it possible to customize the Html.ValidationMessageFor method so that it produces different HTML?
I want to do something similar to:
<div class="field-error-box">
<div class="top"></div>
<div class="mid"><p>This field is required.</p></div>
</div>
I am not sure if it's possible to use paragraph instead of default span, as it may make impossible for validation plugin to place error messages. But for div -s, thats easy - you could write custom html helper.
Something along these lines (may need further testing/coding). You will need to include the namespace of this static extension method in your view, or put this into System.Web.Mvc.Html directly.
public static class Validator
{
public static MvcHtmlString MyValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
TagBuilder containerDivBuilder = new TagBuilder("div");
containerDivBuilder.AddCssClass("field-error-box");
TagBuilder topDivBuilder = new TagBuilder("div");
topDivBuilder.AddCssClass("top");
TagBuilder midDivBuilder = new TagBuilder("div");
midDivBuilder.AddCssClass("mid");
midDivBuilder.InnerHtml = helper.ValidationMessageFor(expression).ToString();
containerDivBuilder.InnerHtml += topDivBuilder.ToString(TagRenderMode.Normal);
containerDivBuilder.InnerHtml += midDivBuilder.ToString(TagRenderMode.Normal);
return MvcHtmlString.Create(containerDivBuilder.ToString(TagRenderMode.Normal));
}
}
As you see, this uses default ValidationMessageFor method, to not interfere with validation-plugin error message processing.
And you use this simply, as default validation message helper
#Html.MyValidationMessageFor(model => model.SomeRequiredField)
I used another way:
public static MvcHtmlString DivValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
return MvcHtmlString.Create(htmlHelper.ValidationMessageFor(expression).ToString().Replace("span", "div"));
}
This way you can use the built in way, but replace the span with a div.
If you need any other overloads of the function, just duplicate as necessary.
You can implement your own ValidationMessageFor helper to emit your desired output or use some javascript to add/modify the rendered HTML code but the custom ValidationMessageFor implementation is the cleaner approach IMHO.
To implement your own ValidationMessageFor helper take a look at the ValidationExtensions.ValidationMessageFor and ValidationMessageHelper methods in the ASP.NET MVC source code.
Implementation Hints
Since GetFormContextForClientValidation is internal you have to work around that implementation by duplicating the internal functionality in your code:
FormContext formContext = htmlHelper.ViewContext.ClientValidationEnabled ? htmlHelper.ViewContext.FormContext : null;
Some other methods are private in ValidationExtensions like GetUserErrorMessageOrDefault you would need to duplicate that code too. What you can do to avoid duplicating code is to let ValidationExtentensions.ValidationMessageFor render the validation message string that is wrapped in a span and afterwards change the rendered string according to your requirements. Keep in mind that "null" is returned in case no error was found and that you'll need the data- HTML attributes in case you have unobtrusive JavaScript enabled.
You can download the ASP.NET MVC 3 source code from here
The only need for change of the default tag generation was in my case, that spans behavior results in anoying margin setups.
I resolved this by using 'display: block'
Maybe this helps some people..
Maybe you can put that code
string propertyName = ExpressionHelper.GetExpressionText(expression);
string name = helper.AttributeEncode(helper.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyName));
if (helper.ViewData.ModelState[name] == null ||
helper.ViewData.ModelState[name].Errors == null ||
helper.ViewData.ModelState[name].Errors.Count == 0)
{
return MvcHtmlString.Empty;
}
on top of the answered function, so that the div doesn't appear on the form load.
I created ValidationMessageAsStringFor which just returns the error message as string. It is basically a simplified version of ValidationMessageFor:
public static MvcHtmlString ValidationMessageAsStringFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
var field = ExpressionHelper.GetExpressionText(expression);
string modelName = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(field);
if (!helper.ViewData.ModelState.ContainsKey(modelName))
{
return null;
}
var modelState = helper.ViewData.ModelState[modelName];
var modelErrors = (modelState == null) ? null : modelState.Errors;
var modelError = ((modelErrors == null) || (modelErrors.Count == 0)) ? null : modelErrors.FirstOrDefault(m => !String.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0];
if (modelError == null)
{
return null;
}
var errorMessage = GetUserErrorMessageOrDefault(helper.ViewContext.HttpContext, modelError, modelState);
return MvcHtmlString.Create(errorMessage);
}
private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState)
{
if (!string.IsNullOrEmpty(error.ErrorMessage))
{
return error.ErrorMessage;
}
if (modelState == null)
{
return null;
}
return modelState.Value?.AttemptedValue;
}
With this in place and after importing the namespace containing the new helper, just create the HTML code you need:
<div class="field-error-box">
<div class="top"></div>
<div class="mid"><p>#Html.ValidationMessageAsStringFor(m => m.FieldName)</p></div>
</div>
Yes, just use a metamodel for the field:
[MetadataType(typeof(YourMetaData))]
public partial class YOURCLASS
{
[Bind(Exclude = "objID")]
public class YourMetaData
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Please enter a name")]
public object Name { get; set; }
}
}
Change your message at the ErrorMessage field :)
Hope this help :)

how to add content to html.label in javascript asp.net mvc

I have a label as
<%= Html.Label("");%>
i want to add content to label at runtime, unfortunately it doesn't take any other parameter to create id property for it. can't i create id property for it just similar to asp:label
thanks,
michaeld
No need to use the HtmlHelper functions always and everywhere if they don't fit your need. They're just supposed to make your life easier, not harder. Use good ole HTML here:
<label id="id_for_label"></label>
If you want to keep using HtmlHelper functions you can always create your own extension methods.
For example:
public static class LabelHelper
{
private static string HtmlAttributes(object htmlAttributes)
{
var builder = new StringBuilder();
foreach (PropertyDescriptor descriptor in
TypeDescriptor.GetProperties(htmlAttributes))
{
builder.AppendFormat(" {0}=\"{1}\" ", descriptor.Name,
descriptor.GetValue(htmlAttributes));
}
return builder.ToString();
}
public static MvcHtmlString MyLabel(this HtmlHelper htmlHelper,
string labelText, object htmlAttributes)
{
var attributes = HtmlAttributes(htmlAttributes);
return MvcHtmlString.Create(
String.Format("<label for=\"{0}\" {1}>{0}</label",
labelText, attributes.Trim()));
}
}
Then you can add a label to a view in the following manner:
<%: Html.MyLabel("Hello, World!", new { #id = "myLabel" })%>
The generated HTML is:
<label for="Hello, World!" id="myLabel">Hello, World!</label>
For MVC 3 such a helper function is already available:
http://msdn.microsoft.com/en-us/library/gg538318(v=VS.99).aspx

ASP.NET MVC 3 Custom HTML Helpers- Best Practices/Uses

New to MVC and have been running through the tutorials on the asp.net website.
They include an example of a custom html helper to truncate long text displayed in a table.
Just wondering what other solutions people have come up with using HTML helpers and if there are any best practices or things to avoid when creating/using them.
As an example, I was considering writing a custom helper to format dates that I need to display in various places, but am now concerned that there may be a more elegant solution(I.E. DataAnnotations in my models)
Any thoughts?
EDIT:
Another potential use I just thought of...String concatenation.
A custom helper could take in a userID as input and return a Users full name...
The result could be some form of (Title) (First) (Middle) (Last) depending on which of those fields are available. Just a thought, I have not tried anything like this yet.
I use HtmlHelpers all the time, most commonly to encapsulate the generation of boilerplate HTML, in case I change my mind. I've had such helpers as:
Html.BodyId(): generates a conventional body id tag for referencing when adding custom css for a view.
Html.SubmitButton(string): generates either an input[type=submit] or button[type=submit] element, depending on how I want to style the buttons.
Html.Pager(IPagedList): For generating paging controls from a paged list model.
etc....
One of my favorite uses for HtmlHelpers is to DRY up common form markup. Usually, I have a container div for a form line, one div for the label, and one label for the input, validation messages, hint text, etc. Ultimately, this could end up being a lot of boilerplate html tags. An example of how I have handled this follows:
public static MvcHtmlString FormLineDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string labelText = null, string customHelpText = null, object htmlAttributes = null)
{
return FormLine(
helper.LabelFor(expression, labelText).ToString() +
helper.HelpTextFor(expression, customHelpText),
helper.DropDownListFor(expression, selectList, htmlAttributes).ToString() +
helper.ValidationMessageFor(expression));
}
public static MvcHtmlString FormLineEditorFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string templateName = null, string labelText = null, string customHelpText = null, object htmlAttributes = null)
{
return FormLine(
helper.LabelFor(expression, labelText).ToString() +
helper.HelpTextFor(expression, customHelpText),
helper.EditorFor(expression, templateName, htmlAttributes).ToString() +
helper.ValidationMessageFor(expression));
}
private static MvcHtmlString FormLine(string labelContent, string fieldContent, object htmlAttributes = null)
{
var editorLabel = new TagBuilder("div");
editorLabel.AddCssClass("editor-label");
editorLabel.InnerHtml += labelContent;
var editorField = new TagBuilder("div");
editorField.AddCssClass("editor-field");
editorField.InnerHtml += fieldContent;
var container = new TagBuilder("div");
if (htmlAttributes != null)
container.MergeAttributes(new RouteValueDictionary(htmlAttributes));
container.AddCssClass("form-line");
container.InnerHtml += editorLabel;
container.InnerHtml += editorField;
return MvcHtmlString.Create(container.ToString());
}
public static MvcHtmlString HelpTextFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string customText = null)
{
// Can do all sorts of things here -- eg: reflect over attributes and add hints, etc...
}
Once you've done this, though, you can output form lines like this:
<%: Html.FormLineEditorFor(model => model.Property1) %>
<%: Html.FormLineEditorFor(model => model.Property2) %>
<%: Html.FormLineEditorFor(model => model.Property3) %>
... and BAM, all your labels, inputs, hints, and validation messages are on your page. Again, you can use attributes on your models and reflect over them to get really smart and DRY. And of course this would be a waste of time if you can't standardize on your form design. However, for simple cases, where css can supply all the customization you need, it works grrrrrrrrreat!
Moral of the story -- HtmlHelpers can insulate you from global design changes wrecking hand crafted markup in view after view. I like them. But you can go overboard, and sometimes partial views are better than coded helpers. A general rule of thumb I use for deciding between helper vs. partial view: If the chunk of HTML requires a lot of conditional logic or coding trickery, I use a helper (put code where code should be); if not, if I am just outputting common markup without much logic, I use a partial view (put markup where markup should be).
Hope this gives you some ideas!
Well in the case of formatting the DisplayFormat attribute could be a nice solution:
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Date { get; set; }
and then simply:
#Html.DisplayFor(x => x.Date)
As far as truncating string is concerned a custom HTML helper is a good solution.
UPDATE:
Concerning your EDIT, a custom HTML helper might work in this situation but there's also an alternative approach which I like very much: view models. So if in this particular view you are always going to show the concatenation of the names then you could define a view model:
public class PersonViewModel
{
public string FullName { get; set; }
}
Now the controller is going to query the repository to fetch the model and then map this model to a view model which will be passed to the view so that the view could simply #Html.DisplayFor(x => x.FullName). The mapping between models and view models could be simplified with frameworks like AutoMapper.
public static HtmlString OwnControlName<T, U>(this HtmlHelper<T> helper, Expression<Func<T, U>> expression, string label_Name = "", string label_Title = "", Attr attr = null)
{
TemplateBuilder tb = null;
string template = null;
if (expression == null) throw new ArgumentException("expression");
obj = helper.ViewData.Model;
tb.Build(obj, expression.Body as MemberExpression, typeof(T), new SimpleTemplate(new TextArea()), label_Name, label_Title, attr);
template = tb.Get();
return new MvcHtmlString(template);
}

How to get the HTML id generated by asp.net MVC EditorFor

I use the HTML Helpers to render my fields:
<%=Html.EditorFor(m => m.User.Surname)%>
and the output would be something like this:
<input class="text-box single-line" id="User_Surname"
name="User.Surname" type="text" value="" />
The helper uses the className + '.' รจ fieldName to build the id.
I need to pass the id to a jQuery function. Is there a way to have it without hardcoding it?
Maybe an helper which can use the ASP.NET MVC2 conventions?
ASP.NET MVC 4 has Html.IdFor() built in that can return this:
#Html.IdFor(m => m.User.Surname)
See this question: get the generated clientid for a form field, this is my answer:
I use this helper:
public static partial class HtmlExtensions
{
public static MvcHtmlString ClientIdFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
return MvcHtmlString.Create(
htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(
ExpressionHelper.GetExpressionText(expression)));
}
}
Use it just as you would any other helper: #Html.ClientIdFor(model=>model.client.email)
I've followed the instructions of Mac's answer here and I've built my own custom extension:
public static class HtmlHelperExtensions
{
public static string HtmlIdNameFor<TModel, TValue>(
this HtmlHelper<TModel> htmlHelper,
System.Linq.Expressions.Expression<Func<TModel, TValue>> expression)
{
return (GetHtmlIdNameFor(expression));
}
private static string GetHtmlIdNameFor<TModel, TValue>(Expression<Func<TModel, TValue>> expression)
{
if (expression.Body.NodeType == ExpressionType.Call)
{
var methodCallExpression = (MethodCallExpression)expression.Body;
string name = GetHtmlIdNameFor(methodCallExpression);
return name.Substring(expression.Parameters[0].Name.Length + 1).Replace('.', '_');
}
return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1).Replace('.', '_');
}
private static string GetHtmlIdNameFor(MethodCallExpression expression)
{
var methodCallExpression = expression.Object as MethodCallExpression;
if (methodCallExpression != null)
{
return GetHtmlIdNameFor(methodCallExpression);
}
return expression.Object.ToString();
}
}
I've imported my application's namespace
<%# Import Namespace="MvcApplication2" %>
and finally I can use my code like this:
<%=Html.HtmlIdNameFor(m=>m.Customer.Name)%>
unless you create your own User.Surname EditorTemplate which you pass in some HTML attributes you won't be able to do this with EditorFor
However you can use TextBoxFor and set the id
<%= Html.TextBoxFor(m => m.User.Surname, new { id = "myNewId" })%>
On the topic of retrieving this element with a jQuery selector you can achieve this without having to hardcode by using some of the ends-with selector. Using that you could probably still use EditorFor and just select it with $("[id^='Surname]")`. If you're trying to select this specific element there's really no way to NOT hardcode SOMETHING in your jQuery code.

Resources