ASP MVC HtmlHelpers
Inline HtmlHelpers
@helper RazorInlineHelper(IEnumerable<string> names)
{
<ul>
@foreach (var name in names)
{
<li>@name</li>
}
</ul>
}
External HtmlHelpers
See Simple Paging
- Output an MvcHtmlString.Create(string)
- Build html with TagBuilder (methods: AddCssClass, MergeAttribute, SetInnerText, propety InnerHTML
- new up a new UrlHelper(helper.ViewContext.RequestContext) for urls
(NB: in Views and Area/Views, not the parent website)
add <system.web.webPages.razor>
<pages [...]>
<namespaces>
<add namespace="MvcApplication1.HtmlHelpers"/>
Built-in Helpers
- @using (Html.BeginForm()) { ...
- For some reason, BeginForm in layout pages doesn't play nice with client validation.
- @Html.TextBox("Search")
... or RadioButton, Hidden, CheckBox, DropDownList
- CheckBox writes a hidden field as well
- ListBox is a multi-select dropdown
- @Html.TextBox("Name") takes value of ViewBag.Name or ViewData["Name"] or Model.Name
- WebGrid for table layout
- Html.AntiForgeryToken() is usually a good idea
Strong typed helpers
The Html.XFor(lamda) functions are strongly typed versions of the Html.X("name") helpers. They use Data Annotations
- Html.TextBoxFor(x => x.Name)
- Html.LabelFor - can use [Display] and [DisplayName] attributes
- Html.DisplayFor - just the text. Can use [DataType(DataType.Date)] and/or UIHint()
- Html.EditorFor - textbox or checkbox as applicable. Can use templates in View/Shared/EditorTemplates
- Html.EditorForModel() - does the entire model. Hide fields with [HiddenInput(DisplayValue=false)], or to exclude, [ScaffoldColumn(false)]
- Html.ValidateFor() / Html.ValidationMessageFor() - validation. Use with Html.ValidationSummary()
Adding attributes:
@Html.TextBoxFor(x => x.Name, new { @class = "long" })
- HTML class attribute = Razor @class="name"
- HTML hyphenated attributes (data-foo) = Razor underscored (data_foo="x")
Note it doesn't work with @Html.EditorFor - you have to use the specific input type like @Html.TextBoxFor.
From MVC 5.1 (January 2014) you can use nested anonymous objects for Html.EditorFor(). If you change between TextFor and EditorFor, it won't work.
new { htmlAttributes = new { @class = "form-control", type="date" } })
@Html.TextBoxFor(model => model.RequestDate,
new { @class = "form-control", type = "date" } )
If you're using the HTML5 date input, you need to use yymmdd format, so easiest is to add data annotations. You'll need ApplyFormatInEditMode so it can bind the posted value too.
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime? RequestDate { get; set; }
ModelState.Remove("HasBeenPosted"); //so the model value isn't overridden
model.HasBeenPosted = true;
Urls
- Url.Content("~/x.css") - for in-page js/css
- @Html.ActionLink("Click me", "Index", "MyController") - for anchors
- The argument order is text, action, [controller]
- Passing querystrings: Html.ActionLink("Click me", "Edit", new { id = 2 })
Radio Buttons
Because they have the same name/id, Html.LabelFor doesn't work. You could rename the id with attributes (
@foreach (var value in Enum.GetValues(typeof(ComputerLanguage)))
{
<label>@Html.RadioButtonFor(m => m.ComputerLanguage, value) @value</label>
}
For the untyped helper, the third parameter indicates if it is checked.
@Html.RadioButton("ComputerLanguage", "CSharp", true)
DropDownList
To render HTML Selects, you can use Html.DropDownList(name, SelectList)/Html.DropDownListFor(m => m.Propety, SelectList)
A SelectList (and MultiSelectList) can be constructed with an IEnumerable of SelectListItems.
A simple string list (selected item is set automatically here)
@Html.DropDownListFor(m => m.Language,
new SelectList(new[] { "EN", "FR", "DE", "ES", "IT", "NL" }))
DropDownListFor entity relations
Entities don't work as well as simple types. The SelectLists are often put on the viewmodel rather than built in the view. If you get a dropdown of System.Web.Mvc.SelectListItem, it's because the SelectList is doing reflection on the SelectListItems you're putting in (you need to name the text/value properties, or just use an IEnumerable of SelectListItems).
@Html.DropDownListFor(m => m.Category,
new SelectList(ViewBag.Categories,
dataTextField: "CategoryName",
dataValueField: "Id",
selectedValue: Model.Category.Id),
"--Please select--")
But this setting the selected item is a little tricky. The easiest way is simply have an Id property (model.CategoryId) and not use the entity relationship.
Controller (or ViewModel):
ViewBag.CategoryList = categories.Select(x => new SelectListItem {Text = x.CategoryName, Value = x.Id.ToString()});
View:
@Html.DropDownListFor(m => m.CategoryId,
(IEnumerable<SelectListItem>)ViewBag.CategoryList,
"--Please select--")
Dropdownlist for Enum
Prior to MVC 5.1, it renders a textbox. After 5.1 (Jan 2014) there is a proper @Html.EnumDropDownListFor
You can get a simple dropdown with an Enum.GetValues:
@Html.DropDownListFor(model => model.State, new SelectList(Enum.GetValues(typeof(Domain.States))))
For more flexible use, prior to MVC 5.1:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;
namespace MvcApplication1
{
public static class CustomHtmlHelpers
{
/// <summary>
/// Dropdowns for enums. @Html.EnumDropDownListFor(m => m.Sex)
/// </summary>
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TEnum>> expression)
{
var metadata =
ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var nonNullableType = metadata.ModelType;
var underlyingType = Nullable.GetUnderlyingType(nonNullableType);
if (underlyingType != null)
{
nonNullableType = underlyingType;
}
var items = (from value in
Enum.GetValues(nonNullableType).Cast<TEnum>()
select new SelectListItem
{
Text = value.ToString(), //could look for [Description] attribute
Value = value.ToString(), //default binder can use string
Selected = value.Equals(metadata.Model)
}).ToList();
if (metadata.IsNullableValueType)
{
var empty = new SelectListItem { Text = string.Empty, Value = string.Empty };
items.Insert(0, empty);
}
return htmlHelper.DropDownListFor(expression, items);
}
}
public static MvcHtmlString EnumRadioListFor<TModel, TEnum>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TEnum>> expression)
{
var metadata =
ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var nonNullableType = metadata.ModelType;
var underlyingType = Nullable.GetUnderlyingType(nonNullableType);
if (underlyingType != null)
{
nonNullableType = underlyingType;
}
var names = Enum.GetNames(nonNullableType);
var sb = new StringBuilder();
//respect the hierarchy
var prefix = htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix;
if (string.IsNullOrWhiteSpace(prefix))
prefix = null;
else
prefix += "_";
foreach (var name in names)
{
//id is format "propertyName_enum".
var id = string.Format("{0}{1}_{2}",
prefix,
metadata.PropertyName,
name);
//use the standard template and turn it into a string
var radio = htmlHelper.RadioButtonFor(expression, name, new { id })
.ToHtmlString();
sb.AppendFormat("<label for=\"{0}\">{1}</label> {2}",
id,
HttpUtility.HtmlEncode(name),
radio);
}
return MvcHtmlString.Create(sb.ToString());
}