Is it possible to override ASP.NET MVC's EditorFor and DisplayFor.
I want to apply some logic as to whether or not to actually display the content or simply output "".
So I want to do something like this (pseudocode):
public static MyEditorFor(this html)
{
if(ICanRender)
{
call HtmlEditorFor(original);
}
else
{
return EmtpyString;
}
}
Is this possible?
It sound like you want an extension:
public static MvcHtmlString MyEditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) {
if(ICanRender)
{
return System.Web.Mvc.Html.EditorExtensions.EditorFor(html, expression);
}
else
{
return MvcHtmlString.Create(string.Empty);
}
}
That codesnipped is untested but it should work.
You can create your own Html helpers as extensions methods for HtmlHelper or HtmlHelper<TModel>. Simply add extension method like below:
public static MvcHtmlString MyEditorFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
}
Of course, you can add additional parameters as needed. Inside this method access all methods of htmlHelper. Return ordinary EditorFor for example:
public static MvcHtmlString MyEditorFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
return htmlHelper.EditorFor(expression);
}
Or you can create your own element (or combination of them) as you need:
public static MvcHtmlString MyEditorFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
TagBuilder div = new TagBuilder("div");
div.AddCssClass("some-css");
MvcHtmlString editor = htmlHelper.TextBoxFor(expression);
div.InnerHtml = editor.ToHtmlString();
return new MvcHtmlString(div.ToString());
}
Read more about TagBuilder here.
You could put the custon models under the "views/shared/EditorTemplates" or "views/shared/DisplayTemplates", and put the files with the [Model name].cshtml.
Ex: DateTime.cshtml
That way all editors and displays with DateTime type defined in your models properties will follow the DateTime.cshtml logic.
The problem is you will have to deal with the HTML in the ViewData.TemplateInfo if you want to retrieve the fields you want.
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'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";
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));
}
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