Not show label for an empty property with LabelFor? - asp.net-mvc-3

I'm using MVC3 w/ Razor and I have a model that has quite a few properties that are sometimes empty.
Other than a custom htmlHelper, or using an if/then in the view for every LabelFor/DisplayFor pair, is there a way to not display the LabelFor/DisplayFor for a property that is empty or null?

No.... You need the above mentioned solutions or additional view models. Sorry!

I created my own helper: LabelAndDisplayFor that checks for null/empty and then chooses to display the field.
public static MvcHtmlString LabelAndDisplayFor<tModel, tValue>(this HtmlHelper<tModel> html, System.Linq.Expressions.Expression<Func<tModel, tValue>> field,
bool hideIfEmpty = false) {
if (hideIfEmpty) {
var v = field.Compile()(html.ViewData.Model);
if (v == null || string.IsNullOrWhiteSpace(v.ToString())) {
return MvcHtmlString.Empty;
}
}
StringBuilder result = new StringBuilder();
result.Append("<div class='display-line'>");
result.Append("<div class='display-label'>");
result.Append(html.LabelFor(field));
result.Append("</div>");
// ... etc ...

Related

How can I set the width of a textbox in asp.net mvc 3?

How can I set the width of a textbox in asp.net mvc 3? this does not work:
#Html.TextBox("Name", new { style = "width:500px" })
try this, this should work..
#Html.TextBox("Name", new { #class= "mySize" })
.mySize
{
width: 500px;
}
also in your code,try adding a semicolon and see if that works, something like this
#Html.TextBox("Name", new { style = "width:500px;" })
I'm surprised the answer given by #Yasser works. Since these extension methods are overloaded with functions that can take several anonymous objects, it's easy inadvertently use the wrong one.
From MSDN, it looks like you're calling this method:
public static MvcHtmlString TextBox(
this HtmlHelper htmlHelper,
string name,
Object value)
where value is:
The value of the text input element. If this value is null, the value of the element is retrieved from the ViewDataDictionary object. If no value exists there, the value is retrieved from the ModelStateDictionary object.
So value is used to populate the input. Instead, I think you want this extension:
public static MvcHtmlString TextBox(
this HtmlHelper htmlHelper,
string name,
Object value,
Object htmlAttributes)
Then, use it like this (pass null for value) to add inline styles to the markup:
#Html.TextBox("Name", null, new { style = "width:500px;" })
Or:
#Html.TextBox("Name", null, new { #class = "myStyle" })

ExpressionHelper.GetExpressionText(expression) not returning the name of my property

I have implemented a MVC Extension to format the numbers in my application. It is based off the code found here. And is as follows
public static MvcHtmlString DecimalBoxFor<TModel>(this HtmlHelper<TModel> html, Expression<Func<TModel, double?>> expression, string format, object htmlAttributes = null)
{
var name = ExpressionHelper.GetExpressionText(expression);
double? dec = expression.Compile().Invoke(html.ViewData.Model);
var value = dec.HasValue ? (!string.IsNullOrEmpty(format) ? dec.Value.ToString(format) : dec.Value.ToString()): "";
return html.TextBox(name, value, htmlAttributes);
}
When I call it with the following line of Razor syntax
#Html.DecimalBoxFor(model => Model.PointAttributes[i].Data.Y,"0.000", new { #class = "span1 number" })
I get an exception because the variable 'name' in my extension is an empty string. I have tried changing the var name line to this but it only gives me the property name of 'Y' and not the full 'Model.PointAttributes[i].Data.Y' that I need to bind the model back for MVC.
var name = ((expression.Body is MemberExpression ?((MemberExpression)expression.Body).Member : ((MemberExpression)((UnaryExpression)expression.Body).Operand).Member)).Name;
Try using this function:
static public string GetExpressionText(LambdaExpression p)
{
if (p.Body.NodeType == ExpressionType.Convert || p.Body.NodeType == ExpressionType.ConvertChecked)
{
p = Expression.Lambda(((UnaryExpression)p.Body).Operand,
p.Parameters);
}
return ExpressionHelper.GetExpressionText(p);
}
This is a known behavior. I have figured out writing my own version of ExpressionHelper that handle that specific case. Now you have two option:
Use the NuGet package:
Install-Package Mariuzzo.Web.Mvc.Extras
Or just grab the source code of the aforementioned ExpressionHelper and glue it into your project.
Here a 'hybrid' one :)
public static void AddModelError<TModel>(this ModelStateDictionary state, Expression<Func<TModel, object>> expression, string message)
{
LambdaExpression lambdaExpression = null;
string fieldName = string.Empty;
if (expression.Body.NodeType == ExpressionType.Convert || expression.Body.NodeType == ExpressionType.ConvertChecked)
{
lambdaExpression = Expression.Lambda(((UnaryExpression)expression.Body).Operand, expression.Parameters);
fieldName = ExpressionHelper.GetExpressionText(lambdaExpression);
} else {
fieldName = ExpressionHelper.GetExpressionText(expression);
}
state.AddModelError(fieldName, message);
}
This one is more compact and probably a better solution:
https://stackoverflow.com/a/12689563/951001
If you can get away without using a nullable type it seems to work (i.e. remove the ? after double, or as in my case decimal). So
Expression<Func<TModel, double?>>
becomes
Expression<Func<TModel, double>>.
If you step through it with the nullable type in place you'll see the expression has a convert() function in it which seems to be the 'problem'. I'm sure like me you would be interested in how to make this function work for nullable types as well.
I know it's closed but for the record;
That's better handled by a template so you can specify what datatype you are using in the model and how it is represented in the template (single responsability).
Also you won't need to modify the MVC framework.
MSDN UiHint attribute

How to return #Html.ActionLink

I am writing a Html Helper for my MVC 3 project.
I want to return the MvcHtmlString like "#Html.ActionLink(xxxxx)", what should I write?
Currently I have this code
public static MvcHtmlString SetFeaturedFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper,Expression<Func<TModel, TValue>> expression)
{
var isFeatured =Convert.ToBoolean(ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model.ToString());
string result = "Html.ActionLink(Delete, DeleteComment, Admin, new { Id = #thisComment.CommentId }, null)";
return MvcHtmlString.Create(result);
}
It return the whole string.... but I want the rendered string. So what should I do? Thanks everyone.
UPDATE
Looks like I can return this directly
See below code
public static MvcHtmlString SetFeaturedFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper,Expression<Func<TModel, TValue>> expression)
{
var isFeatured =Convert.ToBoolean(ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model.ToString());
string indicatorText = (isFeatured) ? "Unset Featured" : "Set Featured";
return htmlHelper.ActionLink(indicatorText, "SetFeaturedIncident", "Admin", null, null);
}
Need to import System.Web.Routing namespace.
Remove the quotes (you want to call the function, not just store the code in a string) and the # (that's Razor, not C# anyways). You might need to change Html to whatever you called the helper parameter in your (presumably) extension method.
Also, Html.ActionLink already returns MvcHtmlString so you can just put it directly after return.

MVC3 Conditionally disable Html.TextBoxFor()

I have a C# .Net web app. In that app I need to conditionally disable Html.TextBoxFor controls (also Html.DropDownListFor controls) based on who is logged into the system. I tried using
#Html.TextBoxFor(model => model.ProposalName, new { #ViewBag.IsDisabled })
Where #ViewBag.IsDisabled is set to either String.Empty or "disabled" in the Controller. However, this renders as IsDisabled='disabled' or IsDisabled="" so the control is not disabled. When I tried
#Html.TextBoxFor(model => model.ProposalName, new { #ViewBag.Disabled })
The control was always disabled even if ViewBag.Disabled contained no text. How can I conditionally disable the Html.TextBoxFor() controls?
Try
#Html.TextBoxFor(model => model.ProposalName, ViewBag.Disabled ? (object)new { disabled="disabled" } : new {})
The solution posted by #epignosisx works, but it may be a problem if you want to add some other attribute because you will have to add it it both objects (the one with disabled and the one now its empty).
Even worse if you have some other bool property because you will have four different objects, each one for each combination.
The best solution here (with a little more code) is to build an extension method for HtmlHelper to receive your boolean property as a parameter.
public static MvcHtmlString TextBoxDisabledFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, bool disabled, object htmlAttributes = null)
{
return TextBoxDisabledFor(htmlHelper, expression, disabled, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString TextBoxDisabledFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, bool disabled, IDictionary<string, object> htmlAttributes)
{
if (htmlAttributes == null)
htmlAttributes = new Dictionary<string, object>();
if (disabled)
htmlAttributes["disabled"] = "disabled";
return htmlHelper.TextBoxFor(expression, htmlAttributes);
}
Here there is another example
I had this same problem and decided to write my own HtmlHelper extension method.
public static MvcHtmlString Disable(this MvcHtmlString helper, bool disabled)
{
if (helper == null)
throw new ArgumentNullException();
if (disabled)
{
string html = helper.ToString();
int startIndex = html.IndexOf('>');
html = html.Insert(startIndex, " disabled=\"disabled\"");
return MvcHtmlString.Create(html);
}
return helper;
}
This will accept a boolean to indicate if the control should be disabled or not. It just appends disabled="disabled" just inside the first > it comes across in a string.
You can use it like below.
#Html.TextBoxFor(model => model.ProposalName).Disable(true)
Here is the method I use, which doesn't require extensions, and doesn't limit you to only one HTML attribute. It assumes there is a boolean property named "Disabled" in your model, but you could put whatever you wanted in there, as long as it evaluates to boolean for the ternary operator:
#Html.TextBoxFor(model => model.Whatever,
new Dictionary<string, object>() {
{ "size", "5" },
{ "class", "someclasshere" },
{ model.Disabled ? "disabled" : "data-notdisabled", "disabled" }
})
The limitation with the standard shortcut notation is that the name of the attribute cannot be dynamic. By creating a dictionary of the correct type, you can then make the attribute name dynamic, and you then pass that dictionary to the textbox as the dictionary of attributes. When the field is not to be disabled, it passes an attribute named "data-notdisabled" instead of one named "disabled", which the browser will then ignore.
Extending #James's answer, I wrote this HtmlHelper extension that updates/removes the disabled attribute if it's already present, or adds it if not:
public static MvcHtmlString Disable(this MvcHtmlString helper, bool disabled) {
string html = helper.ToString();
var regex = new Regex("(disabled(?:=\".*\")?)");
if (regex.IsMatch(html)) {
html = regex.Replace(html, disabled ? "disabled=\"disabled\"" : "", 1);
} else {
regex = new Regex(#"(\/?>)");
html = regex.Replace(html, disabled ? "disabled=\"disabled\"$1" : "$1", 1);
}
return MvcHtmlString.Create(html);
}
It also plays nicely with self-closing tags (like <input />).
Usage is the same:
#Html.TextBoxFor(model => model.PropertyName).Disable(true)
Tested on both #Html.DropDownListFor() and #Html.TextBoxFor().
Using a dictionary for the attributes and conditionally adding the disabled attrib.
<div>
#{
var attribs = new Dictionary<String, object>
{
{ "class", "form-control" }
,{ "style", "display:inline; width:100px;"}
};
if (Model.IsFieldDisabled)
{
attribs.Add("disabled", true);
}
}
#Html.TextBoxFor(m => m.MinValue, attribs)
to
#Html.TextBoxFor(m => m.MaxValue, attribs)
</div>
Adding this really only to add a more literal version of #ITFlyer's answer above. His method of conditionally changing the attribute name rather than the value is crafty and still allows for inlining. However, when I first looked at it I mistakenly thought that it wasn't conditional so I thought a more verbose version was still of value even if it largely just redirects to that answer.
An additional benefit is that a separate variable also allows for reuse over multiple controls (which I needed in my case

Is there a good way of displaying required field indicators when using DataAnnotations in MVC 2?

I've got validation working with DataAnnotations on all my models, but I'd like to display an indicator for required fields on page load. Since I've got all my validation centralized, I'd rather not hard-code indicators in the View. Calling validation on load would show the validation summary. Has anyone found a good way of letting the model determine what's required, but checking it upon rendering the view, similar to Html.ValidationMessageFor?
This is off the top of my head, but it should get you started:
public static MvcHtmlString IsRequiredTextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
if (expression.IsRequired())
return MvcHtmlString.Create(string.Format("{0} [REQUIRED]", helper.TextBoxFor(expression)));
return helper.TextBoxFor(expression);
}
public static bool IsRequired<T, V>(this Expression<Func<T, V>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
throw new InvalidOperationException("Expression must be a member expression");
return memberExpression.Member.GetAttribute<RequiredAttribute>() != null;
}
public static T GetAttribute<T>(this ICustomAttributeProvider provider) where T : Attribute
{
var attributes = provider.GetCustomAttributes(typeof(T), true);
return attributes.Length > 0 ? attributes[0] as T : null;
}
You could add a render method that uses reflection to check for the Required attribute on the field.

Resources