Context =>
${Html.CheckBoxFor(x => x.Foos[i].Checked)}
${Html.LabelFor(x => x.Foos[i].Checked)}
Problem is - i can't provide label text based on Foo.Name.
Is there out of the box method of how to modify metadata, passing in lambda x=>x.Name to change it?
Is creating another HmtlHelper extension method
LabelFor(this htmlhelper, [lambda], value, htmlattribs)
the only way?
Related issue
Eh, whatever. Workaround is acceptable. This issue ain't showstopper =>
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression,
string labelText, object htmlAttributes) where TModel : class
{
TagBuilder builder = new TagBuilder("label");
builder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); // to convert an object into an IDictionary
string value = ExpressionHelper.GetExpressionText(expression); ;
builder.InnerHtml = labelText;
//the replaces shouldnt be necessary in the next statement, but there is a bug in the MVC framework that makes them necessary.
builder.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(value).Replace('[', '_').Replace(']', '_'));
return MvcHtmlString.Create(builder.ToString(TagRenderMode.Normal));
}
Related
I'd like to add some classes to my labels, so I added Label.cshtml Under Shared/EditorTemplates folder. It seems Razor is ignoring it. I've tried DisplayTemplatesfolder too, but it didn't work neither.
Can I have editor template for labels? If no, what's the best way to customize them?
Label.cshtml:
#model object
#Html.LabelFor(m => m, new { #class = "col-xs-4 control-label" })
UPDATE:
This is my code that I took from this post and with some changes from this useful link. It still doesn't work, I don't know what's going on. Can anyone help?
public static class Extensions
{
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
return LabelFor(html, expression, new RouteValueDictionary(htmlAttributes));
}
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
return LabelFor(html, expression, null);
}
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes){
ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string FieldName = ExpressionHelper.GetExpressionText(expression);
string LabelText = metaData.DisplayName ?? metaData.PropertyName ?? FieldName.Split('.').Last();
if (string.IsNullOrEmpty(LabelText))
return MvcHtmlString.Empty;
TagBuilder tag = new TagBuilder("label");
tag.MergeAttributes(htmlAttributes);
tag.SetInnerText(LabelText);
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(FieldName));
tag.Attributes.Add("class", "control-label col-xs-2");
return MvcHtmlString.Create(tag.ToString());
}
}
EditorTemplate's are based on the type of a property (not on the html element they generate). You would need to create your own html helper
public static MvcHtmlString BootstrapLabelFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
{
var attributes = new { #class = "col-xs-4 control-label" };
return helper.LabelFor(expression, attributes);
}
and use it in the view as
#Html.BootstrapLabelFor(m => m.yourProperty)
However this seems an inflexible way to use a helper just to save adding the html attributes in the standard LabelFor() helper. Perhaps it might be more useful to combine the associated textbox and validation message in the one helper as per this answer (Note this answer also shows how to add the namespace to the web.config file
EditorTemplate and DisplayTemplate are based on property type. You'd do this by creating your own HTML helper.
public static class LabelExtensions
{
public static string Label(this HtmlHelper helper, string target, string text)
{
return String.Format("<label for='{0}'>{1}</label>", target, text);
}
}
I would like to be able to do things like:
<input name=#Model.SomeProperty (as the actual property name and not its value) value=#Model.SomeProperty type="text/>
so that model binding still works, but I don't need to use the Razor helpers.
You can add a Helper Method that return your DisplayName:
public static MvcHtmlString GetPropertyName<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression )
{
var metaData = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
string value = metaData.PropertyName ?? expressionHelper.GetExpressionText(expression);
return MvcHtmlString.Create(value);
}
And then you just use: #Html.GetPropertyName(m => m.SomeProperty)
I am working with legacy models that wrap data and meta-data up in a single property. For the purpose of this question, suppose the interface is:
pubic interface ILegacyCheckbox
{
bool Value { get; set; }
bool Editable { get; set; }
}
I want to wrap the CheckBoxFor() extension method with my own,
public static MvcHtmlString LegacyCheckboxFor<TModel>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, ILegacyCheckbox>> expression)
{
// wrap html.CheckBoxFor() method here by extracting the Value
// property and check if Editable is false, in which case add
// an htmlAttribute of "disabled=true"
}
Is there a way to do something like this? Where would I start?
Any help would be appreciated,
Thanks,
Alex
You could try something like this:
public static MvcHtmlString LegacyCheckboxFor<TModel>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, ILegacyCheckbox>> expression)
{
var parameterName = ((MemberExpression)expression.Body).Member.Name;
var compiled = expression.Compile().Invoke(html.ViewData.Model);
if (editable)
return html.CheckBox(parameterName, compiled.Value);
else
return html.CheckBox(parameterName, compiled.Value, new {disabled = "disabled"});
}
You may also wish to cache the compiled expression.
My example uses Html.CheckBox(); I'm not sure how to go about utilising CheckBoxFor(). I haven't got time to investigate it either, but atleast this is somewhere to start.
public static MvcHtmlString LegacyCheckboxFor<TModel>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, ILegacyCheckbox>> expression)
{
MemberExpression memberExpression = expression.Body as MemberExpression;
string parameterName = memberExpression.Member.Name;
var checkbox = expression.Compile().Invoke(html.ViewData.Model);
return new MvcHtmlString(
string.Format(
"<input type=\"checkbox\" name=\"{0}\" id=\"{0}\" value=\"{1}\" {2} />",
parameterName,
checkbox.Value,
checkbox.Editable ? "disabled=true" : string.Empty));
}
I'm hoping that someone can provide a simple, straight forward example of extending the Html.TextBoxFor helper. I would like to include a boolean ReadOnly parameter which will (surprise, surprise, surprise) render the control read only if true. I've seen a few examples which didn't quite do the trick and I've tried the following however, the only signature for TextBoxFor that the HtmlHelper parameter sees is the one I'm creating (am I missing a using statement?):
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes, bool disabled)
{
var values = new RouteValueDictionary(htmlAttributes);
if (disabled)
values.Add("disabled", "true");
return htmlHelper.TextBoxFor(expression, values)); //<-- error here
}
I'm hoping that a simple example will help get me on the right track.
Thanks.
make sure you're using System.Web.Mvc.Html; in your extension class to call HtmlHelper.TextBoxFor<>.
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
object htmlAttributes,
bool disabled)
{
var values = new RouteValueDictionary(htmlAttributes);
// might want to just set this rather than Add() since "disabled" might be there already
if (disabled) values["disabled"] = "true";
return htmlHelper.TextBoxFor<TModel, TProperty>(expression, htmlAttributes);
}
You have one opening and two closing parenthesis on this line. Should be:
return htmlHelper.TextBoxFor(expression, values);
Also to make your HTML a little more standards friendly:
values["disabled"] = "disabled";
I'm wondering if anyone has attempted to write an extension helper to the LabelExtensions.LabelFor HtmlHelper in MVC2? This would be useful for me in that my app requires that I always wrap labels in a <td> tag with a class attribute. Rather than have that code repeated in the View I thought I could write a little extension method:
public static MvcHtmlString RenderLabelFor<TModel, TValue> (
this HtmlHelper html,
Expression<Func<TModel, TValue>> value,
object htmlAttributes
) where TModel : class
{
TagBuilder builder = new TagBuilder("td");
builder.MergeAttributes(new RouteValueDictionary(attributes)); // to convert an object into an IDictionary
builder.InnerHtml = LabelExtensions.LabelFor(html, value).ToString();
return MvcHtmlString.Create(builder.ToString(TagRenderMode.Normal));
}
However I get the error on the LabelFor line:
The type arguments for method 'System.Web.Mvc.Html.LabelExtensions.LabelFor(System.Web.Mvc.HtmlHelper, System.Linq.Expressions.Expression>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Can anyone throw me a bone on this one?
This may be too late to help you, but you need to use the generic version of HtmlHelper in your method signature, like so:
public static MvcHtmlString RenderLabelFor<TModel, TValue> (
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> value,
object htmlAttributes
)
{
...
}
try
public static MvcHtmlString RenderLabelFor<TModel, TValue> ( HtmlHelper html, <Func<TModel, Value>> value, object htmlAttributes) where TModel : class