Class retrieved from an extension of IHtmlHelper that makes building Bootstrap forms mapped to MVC models super easy. Features:
- Each component is placed in a form group Div with Bootstrap class
mb-3
- Support for validation and error messages
- Support for
DisplayAttribute
, includingName
,Prompt
, andDescription
(which is rendered below in a div element with the CSS classform-text
). - Adds the CSS class
is-required
toform-label
if the property for the expression is required. - Extensions for
Controller
andViewDataDictionary
to add Bootstrap alerts, and aRenderAlerts
extension for IHtmlHelper to render alerts in the views. - Pagination links
Given the following model:
public class ExampleViewModel
{
[Display(Prompt = "Placeholder", Description = "Property description")]
[Required(ErrorMessage = "Invalid message")]
public string ModelProperty { get; set; }
}
To build a simple text box:
@{
var bootstrap = Html.Bootstrap();
}
@bootstrap.TextBoxFor(m => m.ModelProperty)
With the model state calculated to be invalid, the above renders out to:
<div class="mb-3">
<label class="form-label is-required" for="Name">Name</label>
<input class="form-control is-invalid " data-val="true" data-val-required="Invalid message" id="Name" name="Name" placeholder="Placeholder" type="text" value="">
<div class="invalid-feedback field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true">
Invalid messge
</div>
<div class="form-text">
Property description
</div>
</div>
Basic textbox:
@bootstrap.TextBoxFor(m => m.ModelProperty)
Textarea:
@bootstrap.TextAreaFor(m => m.ModelProperty, 5, 10)
Textbox for passwords:
@bootstrap.PasswordFor(m => m.ModelPropety)
Datepicker:
@bootstrap.DatePickerFor(m => m.ModelProperty)
Yes/No Radio button group. This renders a group of radio buttons for boolean values. By default
the layout is horizontal, but if you'd rather have a vertical layout for this control, add
RadioButtonLayout.Vertical
. This method supports nullable booleans too. If the value is null,
nothing will be selected by default.
@bootstrap.YesNoFor(m => m.BooleanModelProperty)
@bootstrap.YesNoFor(m => m.BooleanModelProperty, RadioButtonLayout.Vertical) @* vertical layout *@
Checkbox for boolean values:
@bootstrap.CheckboxFor(m => m.BooleanModelProperty)
<select>
dropdown for any list of strings:
@bootstrap.DropDownListFor(m => m.Name, new List<SelectListItem> {
new SelectListItem { Value = "value1", Text = "Display Value 1" },
new SelectListItem { Value = "value2", Text = "Display Value 2" },
new SelectListItem { Value = "value3", Text = "Display Value 3" }
})
<select>
dropdown for enums. This uses the built in IHtmlHelper<TModel>.GetEnumSelectList<TEnum>()
method so this supports DisplayAttribute.Name
right out of the box.
@bootstrap.EnumDropDownListFor(m => m.EnumModelProperty)
If the enum property is nullable and a blank option is required, use NullabelEnumDropDownListFor
. This method supports
adding a custom text value for the empty first option, which is disabled by default, but can be enabled with the
emptyFirstOptionDisabled: false
parameter.
Nullable enums have their own separate helper. If initializing the model, and an enum property has no initial value but
is required for form validation, make the property nullable, add [Required]
, and use NullableEnumDropDownListFor
in
your CSHTML.
@bootstrap.NullableEnumDropDownListFor(m => m.NullableEnumModelProperty, emptyFirstValueText: "Choose...")
// optionally leave the empty first option enabled
@bootstrap.NullableEnumDropDownListFor(m => m.NullableEnumModelProperty, emptyFirstValueText: "Choose...", emptyFirstOptionDisabled: false)
Radio button group for enums. Similar to the Yes/No radio button group, but the options and selected value are
calculated from an Enum type. Layout can be either vertical or horizontal with RadioButtonLayout
. By default it is
horizontal. Set the layout to vertical and use radioGroupHtmlAttributes:
to change the layout to horizontal at a
specific layout.
As with select dropdowns, nullable enums have their own separate helper. If initializing the model, and an enum property
has no initial value but is required for form validation, make the property nullable, add [Required]
, and use
NullableEnumDropDownListFor
in your CSHTML.
// horizontal radio button group
@bootstrap.EnumRadioGroupFor(m => m.EnumModelProperty)
// vertical radio button group that switches to horizontal at medium breakpoint
@bootstrap.EnumRadioGroupFor(m => m.EnumModelProperty, layout: RadioButtonLayout.Vertical, radioGroupHtmlAttributes: new { @class = "flex-md-row gap-md-3" })
// nullable enum property
@bootstrap.NullableEnumRadioGroupFor(m => m.NullableEnumModelProperty)
Checkbox group for enums. As with EnumDropDownListFor
, this uses the IHtmlHelper<TModel>.GetEnumSelectList<TEnum>()
for proper model metadata. This will render a group of checkboxes, and when the form is submitted, the model's property
is set to the list of checked enum values. There is a limitation to reading the value of the model when rending a page.
Because of that limitation, it is best to pass the current value of the model property as a string array, shown below.
@bootstrap.CheckboxGroupFor(m => m.ListOfEnum, selectedItems: Model.ListOfEnum?.Select(o => ((int)o).ToString()).ToArray())
All of the examples above will render what are known as "form groups," or form elements that are accompanied by a label, description (form text), validation, and a bottom margin. If you are building a specialty form element and want the basic control without labels, form text, validation, or margin, use the control methods for each component listed above:
TextBoxControlFor(expression, htmlAttributes, format)
TextAreaControlFor(expression, rows, columns, inputAttributes)
PasswordControlFor(expression, inputAttributes)
YesNoControlFor(expression, layout)
CheckboxGroupControlFor(expression, selectedItems)
DropDownListControlFor(expression, items, displayEmptyFirstValue, emptyFirstValueText, selectHtmlAttributes)
EnumDropDownListControlFor(expression, selectHtmlAttributes)
NullableEnumDropDownListControlFor(expression, selectHtmlAttributes)
EnumRadioGroupControlFor(expression, layout, radioGroupHtmlAttributes)
NullableEnumRadioGroupControlFor(expression, layout, radioGroupHtmlAttributes)
Use LabelFor(expression, htmlAttributes)
to take advantage of Bootstrap labels in a custom form group.
All extensions that support the anonymous object for adding additional HTML attributes also
support passing HTML attributes of type IDictionary<string,object>
. Note that passing
IDictionary<string,string>
will exhibit unintended behavior.
@bootstrap.TextBoxFor(m => m.Property, labelHtmlAttributes: new { id = "id" }) @* Adds HTML Attributes to the Label *@
@bootstrap.TextBoxFor(m => m.Property, containerHtmlAttributes: new { id = "id" }) @* Adds HTML Attributes to the containing <div> *@
@bootstrap.TextBoxFor(m => m.Property, htmlAttributes: new { id = "id" }) @* Adds HTML Attributes to the <input> element *@
@bootstrap.YesNoFor(m => m.BooleanModelProperty, new Dictionary<string, object> {
{ "data-attribute", "attribute value" }
}) @* Also adds HTML Attributes to the input *@
Many of the rendered HTML elements have default class names. When additional classes are provided through the given HTML attributes object, the classes are appended to the default class list.
@bootstrap.TextBoxFor(m => m.Property, inputAttributes: new { @class = "custom-input" })
The resulting class list will be form-control custom-input
.
There are three elements where elements can usually be applied:
inputAttributes
— The main input element. By default this input has theform-control
class as well as whatever attributes are applied viaIHtmlHelper<T>.TextBoxFor()
.labelHtmlAttributes
— The label for the control. Usually this has theform-label
CSS class as well asis-required
if the model metadata marks it as such. Other attributes are applied viaIHtmlHelper<T>.LabelFor()
.validationHtmlAttributes
- The validation message rendered below the control. By default this element has classinvalid-feedback
as well asdata-*
attributes for jQuery validation.containerHtmlAttributes
— The containing<div>
element. This is usually a container with just amb-3
CSS class.
The following table lists out which components and methods support each attribute:
Method | Input Attributes | Label Attributes | Validation Attributes | Container Attributes |
---|---|---|---|---|
TextBoxFor |
✅ (htmlAttributes ) |
✅ | ✅ | ✅ |
TextAreaFor |
✅ | ✅ | ✅ | ✅ |
PasswordFor |
✅ | ✅ | ✅ | ✅ |
DatePickerFor |
✅ | ✅ | ✅ | ✅ |
YesNoFor |
❌ | ✅ | ✅ | ✅ |
CheckboxFor |
❌ | ✅ | ✅ | ✅ |
CheckboxGroupFor |
❌ | ✅ | ✅ | ✅ |
DropDownListFor |
✅ (selectHtmlAttributes ) |
✅ | ✅ | ✅ |
EnumDropDownListFor |
✅ (selectHtmlAttributes ) |
✅ | ✅ | ✅ |
NullableEnumDropDownListFor |
✅ (selectHtmlAttributes ) |
✅ | ✅ | ✅ |
TextBoxControlFor |
✅ (htmlAttributes ) |
❌ | ❌ | |
TextAreaControlFor |
✅ | ❌ | ❌ | |
PasswordControlFor |
✅ | ❌ | ❌ | |
YesNoControlFor |
❌ | ❌ | ❌ | |
CheckboxGroupControlFor |
❌ | ❌ | ❌ | |
DropDownListControlFor |
✅ (selectHtmlAttributes ) |
❌ | ❌ | |
EnumDropDownListControlFor |
✅ (selectHtmlAttributes ) |
❌ | ❌ | |
NullableEnumDropDownListControlFor |
✅ (selectHtmlAttributes ) |
❌ | ❌ | |
FormGroupFor |
❌ | ✅ | ✅ | ✅ |
Labels for the form groups are automatically given the CSS class is-required
if the property
of the model is required. To add a required indicator, typically a red asterisk, add this CSS:
.form-label.is-required::after {
content: '*';
color: var(--bs-danger);
padding-left: 0.10rem;
}
Alerts can be added to views with the AddAlert
extension. This extension is available on the Controller and ViewDataDictionary classes:
# Within MVC Controller
AddAlert(BootstrapColor.Success, "Alert message");
# With view data:
ViewData.AddAlert(BootstrapColor.Success, "Alert message");
Alerts can be added to TempData to save the alert for the next request. Useful when showing an alert after redirecting to another action:
TempData.AddAlert(BootstrapColor.Success, "Alert Message");
Alert types are available for each Bootstrap color classes:
public enum BootstrapColor
{
Primary,
Secondary,
Success,
Danger,
Warning,
Info,
Light,
Dark
}
In your views, render your alerts as follows:
@Html.RenderAlerts()
To prevent rendering views from TempData, pass false
:
@Html.RenderAlerts(false)
Note: The content of the alerts are not HTML encoded. RAW HTML will be rendered. Encode any user-generated strings you
pass to AddAlert
.
This package features the BsPagination
class with methods for rendering Bootstrap pagination links.
@BsPagination.Paginate(5, 10, page => Url.Action("Index", "Home", new { page })!)
The above will render the following:
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center ">
<li class="page-item "><a class="page-link" href="/?page=4">Previous</a></li>
<li class="page-item "><a class="page-link" href="/?page=1">1</a></li>
<li class="page-item"><span class="page-text">…</span></li>
<li class="page-item "><a class="page-link" href="/?page=4">4</a></li>
<li class="page-item active"><a class="page-link" href="/?page=5">5</a></li>
<li class="page-item "><a class="page-link" href="/?page=6">6</a></li>
<li class="page-item"><span class="page-text">…</span></li>
<li class="page-item "><a class="page-link" href="/?page=10">10</a></li>
<li class="page-item "><a class="page-link" href="/?page=6">Next</a></li>
</ul>
</nav>
Paginate
takes the following parameters:
int currentPage, int totalPages, Func<int, string> buildHref, string ulClasses = "", string nonLinkClass = "page-text"
Parameter Name | Description |
---|---|
currentPage |
The current page. This page will be indicated with the active CSS class. |
totalPages |
Total pages available. If you have the count of total items, the total number of pages can be calculated with BsPagination.TotalPages(totalItems, itemsPerPage) |
buildHref |
A lambda function that takes an integer as a parameter and returns a string. This method is called for each page item. Use this to build the href URL for each page. |
ulClasses |
CSS classes for the <ul> element that is rendered. The <ul> element has pagination justify-content-center . The value of this parameter is added to this class list. |
nonLinkClass |
The class for the <span> element that is rendered for non-link page items. By default the class is page-text . |
This pagination method renders a page item with an ellipsis character and no links. This indicates separation between the first page and the previous page as well as separation between the next page and the last page. However, Bootstrap does not have good styling for this type of element, so we recommend adding this to your CSS:
.page-text {
position: relative;
display: block;
padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);
font-size: var(--bs-pagination-font-size);
background-color: var(--bs-pagination-bg);
border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);
color: var(--bs-secondary);
}
.page-item:not(:first-child) .page-text {
margin-left: calc(var(--bs-border-width) * -1);
}
BsPagination
provides three utility methods for making calculations related to pagination:
Method | Description |
---|---|
TotalPages(int totalItems, int itemsPerPage) |
Calculate the number of pages based on the total number of items. |
MinMax(int page, int totalPages) |
Returns page unless it is below zero or above the value of totalPages , in which case it will return those values. |
Offset(int page, int perPage, int totalPages) |
Returns the SQL OFFSET value (or LINQ Skip() value) based on the number of items per page. MinMax is called in this method, so validation is taken care of. |