ASP.NET MVC, CheckBoxFor() attribute problems - asp.net-mvc

When I use CheckBoxFor() HtmlHelper like this:
#Html.CheckBoxFor(x => Model.IsDeleted, new { myCustomAttribute = "myCustomAttribute" })
my checkbox have attribute, but hidden element not have.
<input myCustomAttribute="myCustomAttribute" id="IsDeleted" name="IsDeleted" type="checkbox" value="true">
<input name="IsDeleted" type="hidden" value="false">
How add atribute to hidden element? I do not want to write custom HTML.

In my opinion the easiest way to achieve such functionality is to create custom helper :) Below simple helper from http://20fingers2brains.blogspot.com/2013/05/custom-checkbox-html-helper-in-mvc3.html :
public static class CustomCheckBoxHelper
{
//This helper accepts name attribute. This method in turns calls our second overload.
public static MvcHtmlString Custom_Checkbox(this HtmlHelper helper, string name)
{
return Custom_Checkbox(helper, name, false);
}
//This helper accepts name and isChecked boolean attribute.
public static MvcHtmlString Custom_Checkbox(this HtmlHelper helper, string name,bool isChecked)
{
return Custom_Checkbox(helper, name, isChecked, null);
}
//This overload accepts name, isChecked and htmlAttributes as parameter.
public static MvcHtmlString Custom_Checkbox(this HtmlHelper helper, string name,bool isChecked,object htmlAttributes)
{
//Creating input control using TagBuilder class.
TagBuilder checkbox = new TagBuilder("input");
//Setting its type attribute to checkbox to render checkbox control.
checkbox.Attributes.Add("type", "checkbox");
//Setting the name and id attribute.
checkbox.Attributes.Add("name", name);
checkbox.Attributes.Add("id", name);
//Adding the checked attibute based on parameter received.
if (isChecked)
checkbox.Attributes.Add("checked", "checked");
//Merging the other attributes passed in the attribute.
checkbox.MergeAttributes(new RouteValueDictionary(htmlAttributes));
return MvcHtmlString.Create(checkbox.ToString(TagRenderMode.Normal));
}
}

Related

Cannot assign attributes to form using Html.BeginForm

I have a form begun with the Html.BeginForm() helper method:
#using (Html.BeginForm(
null,null, new { #id = "MaintenanceForm", #class = "datatable", #nonvalidate="nonvalidate" }
))
and the form is rendered as:
<form action="(controller's name)/(current action's name)/MaintenanceForm?class=datatable" method="post">
The attributes such as id, class, and nonvalidate aren't assigned. Also I do not want a default Http Method. What can I do?
Your current code is matching with the below overload of BeginForm method
public static MvcForm BeginForm(
this HtmlHelper htmlHelper,
string actionName,
string controllerName,
object routeValues
)
The third parameter here is an object for the route values. These will be added as the querystring key value(s) to your form's action attribute value. That is the reason you are seeing those big url as the action attribute value.
If you want to specify the html attributes( Id,class etc), Use this overload which has a fourth parameter which takes the html attributes. The third parameter is the FormMethod.
public static MvcForm BeginForm(
this HtmlHelper htmlHelper,
string actionName,
string controllerName,
FormMethod method,
object htmlAttributes
)
This should work.
#using (Html.BeginForm("Create", "Post",FormMethod.Post,
new { #id = "MaintenanceForm", #class = "datatable", #nonvalidate = "nonvalidate" }))
{
}
Replace Create and Post with your action method name and controller name.

ASP.NET MVC - Using Model in Custom HTML Helper [duplicate]

This question already has answers here:
Refactor similar CHTML that renders varying properties using EditorFor and LabelFor
(2 answers)
Closed 6 years ago.
I am building an app with ASP.NET MVC and Bootstrap. In my app, I have a view with a Model that looks like this:
public class EntryModel
{
[Required(ErrorMessage="Please enter the name.")]
[Display(Name="Name")]
public string Name { get; set; }
[Required (ErrorMessage="Please enter the description.")]
[Display(Name = "Description")]
public string Description { get; set; }
}
In this app, I've also defined a custom html helper that looks like this:
public static class MyHelpers
{
public static MvcHtmlString MyTextBox(this HtmlHelper helper)
{
var sb = new StringBuilder();
sb.Append("<div class=\"form-group\">");
sb.Append("<label class=\"control-label\" for=\"[controlId]\">[labelValue]</label>");
sb.Append("<input class=\"form-control\" id=\"[controlId]\" name=\"controlId\" type=\"text\" value=\"[propertyValue]\">");
sb.Append("</div>");
return MvcHtmlString.Create(sb.ToString());
}
}
I'm using this helper and model in my Razor view, like this:
#model EntryModel
<h2>Hello</h2>
#using (Html.BeginForm("Add", "Entry", new {}, FormMethod.Post, new { role="form" }))
{
#Html.MyTextBox()
}
I am trying to generate the labelValue, controlId, and propertyValue values in the helper from the properties of the Model. For example, I'd like to use #Html.MyTextBoxFor(m => m.Name) and have the helper generate something like this:
<div class="form-group">
<label class="control-label" for="Name">Name</label>");
<input class="form-control" id="Name" name="Name" type="text" value="Jon">
</div>
Essentially, I'm not sure how to get my model information into my html helper.
Use this example as reference:
public static MvcHtmlString AutoSizedTextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
var attributes = new Dictionary<string, Object>();
var memberAccessExpression = (MemberExpression)expression.Body;
var stringLengthAttribs = memberAccessExpression.Member.GetCustomAttributes(
typeof(System.ComponentModel.DataAnnotations.StringLengthAttribute), true);
if (stringLengthAttribs.Length > 0)
{
var length = ((StringLengthAttribute)stringLengthAttribs[0]).MaximumLength;
if (length > 0)
{
attributes.Add("size", length);
attributes.Add("maxlength", length);
}
}
return helper.TextBoxFor(expression, attributes);
}
And you can call it in the view like this:
#Html.AutoSizedTextBoxFor(x => x.Address2)

Is there a way to automatically display “*” required icon beside the [Required] fields

I have finished my asp.net MVC web application, and I have been using the data annotation [Required] to mention that the field is required. But currently the required fields does not have any indication that they are required, unless the user tried to submit the form. So is there way to force my Razor view to display a red “” beside any field that have [Required] defined on it? OR I need to manually add the “” icon ?
Thanks
After I got burned by the Bootstrap 2 to 3 upgrade, where they pretty much completely changed the HTML for form controls, I've been putting the entire form group in editor templates instead of just the field. Here's an example:
<div class="form-group">
#Html.Label("", new { #class = string.Format("control-label col-md-2{0}", ViewData.ModelMetadata.IsRequired ? string.Empty : " optional") })
<div class="col-md-6">
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue.ToString(), new { type = "email", #class = "form-control" })
#Html.ValidationMessage("")
</div>
</div>
What's important here for you is the string.Format in Html.Label. I'm using the ViewData.ModelMetadata.IsRequired to add an "optional" class if it's false. Bootstrap makes the labels bold by default, so as a required indicator, I make optional field labels normal (non-bold). However, adding a * is a little more difficult. You could use the same thing I'm doing here to add an additional span tag:
#if (ViewData.ModelMetadata.IsRequired)
{
<span class="requiredIndicator>*</span>
}
#Html.Label(...)
...
The potential problem is that that won't actually be inside your <label> tag, so you might have to do some extra styling work to make it look right depending on the styles you apply to the labels.
An alternative is to create your own HtmlHelper to return a label with a required indicator. Here's some sample code for that:
public static MvcHtmlString RequiredIndicatorLabelFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> modelProperty,
object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(modelProperty, html.ViewData);
var labelText = metadata.IsRequired ? string.Format("* {0}", metadata.GetDisplayName()) : metadata.GetDisplayName();
return html.LabelFor(modelProperty, labelText, htmlAttributes);
}
You can also write a custom label helper for this purpose
public static MvcHtmlString CustomLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression,
IDictionary<string, object> htmlAttributes = null )
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var propertyName = ExpressionHelper.GetExpressionText(expression);
var builder = new TagBuilder("label");
builder.Attributes.Add("for", TagBuilder.CreateSanitizedId(htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyName)));
var labelText = string.Format("{0}{1}", metadata.IsRequired ? "*" : string.Empty,
string.IsNullOrEmpty(metadata.DisplayName)
? metadata.PropertyName
: metadata.DisplayName);
builder.SetInnerText(labelText);
builder.MergeAttributes<string, object>(htmlAttributes, true);
return new MvcHtmlString(builder.ToString());
}
Now when used CustomLabelFor on a property with Required attribute, it will append * in fort of the label text.
#Html.CustomLabelFor(m => m.YourRequiredField)

My FormMethod.Get request is sending multiple values how do I fix this in ASP.net MVC?

I have a mvc5 webpage that has a form method that when the checkbox is checked submits the form and sends the get request. The request header submits the CarNumber and CarId as expected but the checkbox value is sent twice in the header why is this happening? How can I fix the issue.
Header value repeated like this
ShowAllDesignReviews=true&ShowAllDesignReviews=false
#using (Html.BeginForm("Index", "DesignReview", FormMethod.Get))
{
#Html.Hidden("CarNumber", "CarNumber")
#Html.Hidden("CarId", "CarId")
#Html.CheckBoxFor(model => Model.ShowAllDesignReviews, new { #onchange = "this.form.submit()" })
}
This is how CheckBoxFor helper in MVC generates the html, for easy model binding
<input name="ShowAllDesignReviews" type="checkbox" value="true" />
<input name="ShowAllDesignReviews" type="hidden" value="false" />
If you dont select check box, the field will not be posted. To make the value(false) to be passed they used Hidden.
Refer asp.net mvc: why is Html.CheckBox generating an additional hidden input
Better to have your controller that accepts the model
public ActionResult Save(Design model)
{
var boolSet=model.ShowAllDesignReviews;
}
See Murali's answer as to why this happens.
I had some similar scenarios where I wanted to have a "standard" checkbox, so I wrote the following HtmlHelper:
public static class StandardCheckboxForHelper
{
public static MvcHtmlString StandardCheckboxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression)
{
return html.StandardCheckboxFor(propertyExpression, null);
}
public static MvcHtmlString StandardCheckboxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression, object htmlAttributes)
{
return html.StandardCheckboxFor(propertyExpression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString StandardCheckboxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression, IDictionary<string, object> htmlAttributes)
{
var propertyName = html.NameFor(propertyExpression).ToString();
var propertyId = html.IdFor(propertyExpression).ToString();
var propertyValueString = html.ValueFor(propertyExpression).ToString();
var propertyValue = ModelMetadata.FromLambdaExpression(propertyExpression, html.ViewData).Model;
var unobtrusiveValidationAttributes = html.GetUnobtrusiveValidationAttributes(propertyName, ModelMetadata.FromLambdaExpression(propertyExpression, html.ViewData));
var checkbox = new TagBuilder("input");
checkbox.MergeAttribute("type", "checkbox");
checkbox.MergeAttribute("id", propertyId);
checkbox.MergeAttribute("name", propertyName);
checkbox.MergeAttributes(unobtrusiveValidationAttributes);
checkbox.MergeAttributes(htmlAttributes);
bool propertyValueConverted;
if ((propertyValue is bool) && (bool) propertyValue)
checkbox.MergeAttribute("checked", "checked");
else if ((propertyValue is bool?) && ((bool?)propertyValue).GetValueOrDefault(false))
checkbox.MergeAttribute("checked", "checked");
else if (bool.TryParse(propertyValueString, out propertyValueConverted) && propertyValueConverted)
checkbox.MergeAttribute("checked", "checked");
return checkbox.ToHtml();
}
}
Usage is the same as the CheckboxFor helper, and it will only render one simple checkbox.

Custom MVC template for enum to drop down list

I have created several MVC templates for the EditorFor and DisplayFor helper methods to style things the way I wanted using the Twitter Bootstrap framework. I now have a working solution for all the bits I need, but would like to generalize one part I set up to show a list of states. I have a State enum (with a list of all US states) that I display in a drop down for a users address. I used the [DataType] attribute to get MVC to use my State.cshtml template.
[Required]
[Display(Name = "State")]
[DataType("State")]
public State State { get; set; }
So it works nicely, but I would like to change it so that I can do something like DataType("Enum") or some other way to hit this template generically for all enums.
The template looks like this:
#using System
#using System.Linq
#using Beno.Web.Helpers
#using TC.Util
#model Beno.Model.Enums.State
<div class="control-group">
#Html.LabelFor(m => m, new {#class = "control-label{0}".ApplyFormat(ViewData.ModelMetadata.IsRequired ? " required" : "")})
<div class="controls">
<div class="input-append">
#Html.EnumDropDownListFor(m => m)
<span class="add-on">#(new MvcHtmlString("{0}".ApplyFormat(ViewData.ModelMetadata.IsRequired ? " <i class=\"icon-star\"></i>" : "")))</span>
</div>
#Html.ValidationMessageFor(m => m, null, new {#class = "help-inline"})
</div>
</div>
The EnumDropDownListFor is a helper method I posted about before and that works generically with any enum. What I don't know is how would I change this template to take a generic enum as the model object?
UPDATE: For completeness I include a listing of the EnumDropDownListFor method:
public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null) where TProperty : struct, IConvertible
{
if (!typeof(TProperty).IsEnum)
throw new ArgumentException("TProperty must be an enumerated type");
var selectedValue = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model.ToString();
var selectList = new SelectList(from value in EnumHelper.GetValues<TProperty>()
select new SelectListItem
{
Text = value.ToDescriptionString(),
Value = value.ToString()
}, "Value", "Text", selectedValue);
return htmlHelper.DropDownListFor(expression, selectList, htmlAttributes);
}
Changing the model type to Enum produces the following error on the line with the call to the helper method:
CS0453: The type 'System.Enum' must be a non-nullable value type in order to use it as parameter 'TProperty' in the generic type or method 'Beno.Web.Helpers.ControlHelper.EnumDropDownListFor<TModel,TProperty>(System.Web.Mvc.HtmlHelper<TModel>, System.Linq.Expressions.Expression<System.Func<TModel,TProperty>>, object)'
Then if I remove the check if TProperty is an enum and the struct where constraint, I get a compile error on the line where I am trying to get the enum values of:
System.ArgumentException: Type 'Enum' is not an enum
I wonder if it's just not possible to do what I am trying here.
You could just create an EditorTemplate Enum.cshtml
All you would have to do is change this line :
#model Beno.Model.Enums.State
For this :
#model System.Enum
You will then be able to use any Enum with it.
The catch: the engine can't infer the base class of an item thus, TestEnum won't be assigned the Enum template, so you would have to call it explicitly :
#Html.EditorFor(model => model.EnumValue, "Enum")
Not sure if I understand exactly what you mean, but try this:
#Html.DropDownListFor(model => model.EnumName, new SelectList(Enum.GetValues(typeof(Namespace.Models.EnumName))))
EnumName = State in your case.
I've used the above to get an enum into a drop down list using Twitter Bootstrap.
I too have been trying to achieve this.
Is the idea that you want to be able to use one template for all Enum types in all your models.
This way you have an Enum Template in the EditorTemplates folder that allow you to display them as drop down lists.
I have been following this article. http://blogs.msdn.com/b/stuartleeks/archive/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums.aspx
The issue you have is that your template passes the type of System.Enum in the TModel and TProperty
Expression<Func<TModel, TProperty>> expression
Then when you perform the following below TProperty is of Type System.Enum not Beno.Model.Enums.State
EnumHelper.GetValues<TProperty>()
To get around this I do not bother looking at TProperty as it does not give me the right type.
Instead I look at the metadata.ModelType.
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
This gives me the correct type but you can't use these in within the Covariance Derived class
EnumHelper.GetValue<metadata.ModelType> //This does not work.
So I rewrote the body to not use any generics.
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var values = Enum.GetValues(metadata.ModelType);
List<SelectListItem> items = new List<SelectListItem>();
foreach (var v in values)
{
items.Add(new SelectListItem
{
Text = Regex.Replace(v.ToString(), "([A-Z][a-z])", " $1").Trim(),
Value = v.ToString(),
Selected = v.Equals(metadata.Model)
});
}
return htmlHelper.DropDownListFor(expression, items);
}
You may need to change the method signature to include your htmlattributes.
As others show, writing a custom helper is the way to go. This is exactly what was done in TwitterBootstrapMVC. Among other helpers it has a helper DropDownListFromEnumFor(...), which you'd use like so:
#Html.Bootstrap().DropDownListFromEnumFor(m => m.SomeEnum)
or
#Html.Bootstrap().DropDownListFromEnum("SomeEnum")
The cool thing about BMVC is that you can customize the dropdown with extension methods some of which are for regular html and others are Bootstrap specific. Below are some of them:
#(f.ControlGroup().DropDownListFromEnumFor(m => m.SomeEnum)
.Append("something")
.AppendIcon("glyphicon glyphicon-chevron-right")
.Class("cool-dd")
.OptionLabel("-- Select --")
.Tooltip("cool tooltip"))
Oh, and yeah, the example above will generate full control-group - input, label, and validation message.
Disclaimer: I'm the author of TwitterBootstrapMVC

Resources