This project aims to help .NET MVC developers to write twitter bootstrap related code faster. If you'd like a jump start, watch 6 minutes video either on YouTube or on CodeProject.tv explaining what TwitterBootstrapMVC does.
If you like this project, please up vote it on StackOverflow.
Twitter Bootstrap is a great front-end framework and the documentation for this product is outstanding. Every use case is very well documented and is available for the developer.
The problem that I encountered with it when I was working on PersonalityMatch was that I cannot possibly memorize all this documentation. Especially this was true during writing of forms. I cannot remember how I should structure inputs and what classes I should apply to them in every situation. Even though the documentation is very well structured, it is time consuming to go through it and find the example you need.
This is why I have created this library of html extensions that aims to make writing Twitter Bootstrap related code easier. Please note that this library does not have a helper for every little thing that Twitter Bootstrap offers. Some of the components of the Bootstrap framework are very flexible in their nature, which is why writing helpers to handle these would probably force developer go to this page and see the documentation on the syntax, which would defeat the purpose of this library.
I welcome suggestions on improving this library.
In order to use TwitterBootstrapMVC you have to be familiar with the general way Twitter Bootstrap works and with what it has to offer.
This library includes two versions of each input html helper just like there are two versions of regular MVC HTML helpers @Html.TextBoxFor(m => m.FirstName) and @Html.TextBox("FirstName"). For the sake of simplicity I will be showing examples using TextBoxFor(...).
@Html.TextBoxFor(m => m.FirstName)
@Html.TextBox("FirstName")
TextBoxFor(...)
@Html.Bootstrap().LabelFor(x => x.UserName) @Html.Bootstrap().TextBoxFor(m => m.UserName) @Html.Bootstrap().PasswordFor(m => m.Password) @Html.Bootstrap().FileFor(m => m.File) @Html.Bootstrap().CheckBoxFor(m => m.IsActivated) @Html.Bootstrap().RadioButtonFor(m => m.Gender, "male") @Html.Bootstrap().DropDownListFor(m => m.State, Model.UsaStates) @Html.Bootstrap().TextAreaFor(m => m.Description)
One might say: "These look exactly like regular MVC helpers. Why were they created in the first place?" Each bootstrap input usually has a special class associated with it. This is especially true when inputs are coupled with labels (Ex: checkbox). You will never need to look at the documentation to see what class you need to apply to an input or a label. Also these methods will allow for fluent syntax which is discussed next.
Each html helper can be customized by the use of fluent syntax.
@Html.Bootstrap().LabelFor(x => x.Amount).LabelText("Amount Left").ShowRequiredStar(true) @Html.Bootstrap().TextBoxFor(m => m.Amount).Placeholder("amount in dollars").Prepend( "$").Append(".00").Append(Html.Bootstrap().Button().Text("Save"))
These two lines of code above will give you the following HTML output:
<label for="Amount"> Amount Left<span class="required">*</span> </label> <div class="input-prepend input-append"> <span class="add-on">$</span> <input id="Amount" name="Amount" placeholder="amount in dollars" type="text" value="10"> <span class="add-on">.00</span> <button class="btn" type="button">Save</button> </div>
Which will look like this:
Each input html helper can be extended with .Label(). Once you apply it to the basic input, this input will now be coupled with the label. This will start a new chain in your fluent syntax so that before this method all the extension methods belong to the input and after this method all the extension methods belong to the label.
.Label()
@Html.Bootstrap().TextBoxFor(m => m.UserName).Size( InputSize.Medium).Label().HtmlAttributes(new { @class = "special-label" })
This is the section which, in my opinion brings the most value to a developer.
Often a tabular type of a form layout is needed where labels are to the left of the inputs and are aligned to each other. This is where you can use @Html.Bootstrap().ControlGroup(). Please note that you need a form with a class "form-horizontal" in order for it to display properly.
@Html.Bootstrap().ControlGroup()
@using (Html.BeginForm("actionName", "controllerName", FormMethod.Post, new { @class = "form-horizontal" })) { @Html.Bootstrap().ControlGroup().TextBoxFor(m => m.UserName) @Html.Bootstrap().ControlGroup().TextBoxFor(m => m.FirstName) @Html.Bootstrap().ControlGroup().TextBoxFor(m => m.LastName) @Html.Bootstrap().ControlGroup().CheckBoxFor(m => m.IsActivated) @Html.Bootstrap().ControlGroup().DropDownListFor(m => m.State, Model.UsaStates) @Html.Bootstrap().ControlGroup().TextAreaFor(m => m.Description) using(Html.Bootstrap().Container().BeginFormActions()) { <div> @Html.Bootstrap().SubmitButton() @Html.Bootstrap().ActionLinkButton("Go Back", "previousAction", "controller") </div> } }
This will give you a nice formatted form. I won't post the html output. It is more than I ever want to see or especially type. However, for the end user it will look like this:
Notice that you do not need to use .Label() on each input when you are using .ControlGroup() unless you want to customize the label. Label will be created automatically. All the rules described previously apply to the ControlGroup helper methods.
.ControlGroup()
ControlGroup
In the example above I've also introduced a disposable .Bootstrap().Container().BeginFormActions(). You can put any html inside the curvy brackets there. The output in this case will be:
.Bootstrap().Container().BeginFormActions()
<div class="form-actions"> <button class="btn" type="submit">Submit</button> <a class="btn" href="http://www.codeproject.com/controller/previousAction">Go Back</a> </div>
As you can see I've also used there methods .Bootstrap().SubmitButton() and .Bootstrap().ActionLinkButton(...). These are also designed so that you don't have to lookup classes specific to your particular case. More about these below.
.Bootstrap().SubmitButton()
.Bootstrap().ActionLinkButton(...)
.Placeholder()
@Html.Bootstrap().TextBoxFor(m =< m.UserName).Placeholder("Text for placeholder")
... or by using parameterless overload: .Placeholder() and using data annotation [Display].
[Display].
So if you have the following decorated property:
[Display(Name = "Label for User Name", Prompt = "Placeholder for User Name")] public string UserName { get; set; }
@Html.Bootstrap().ControlGroup().TextBoxFor(m => m.UserName).Placeholder()
<div class="control-group"> <label class="control-label" for="UserName"> Label for User Name <span class="required" style="visibility:hidden;">*</span> </label> <div class="controls"> <input id="UserName" name="UserName" placeholder="Placeholder for User Name" type="text" value=""> <span class="help-inline"> <span class="field-validation-valid" data-valmsg-for="UserName" data-valmsg-replace="true"></span> </span> </div> </div>
.ActionLinkButton is a regular link with a specific bootstrap class that makes it look like a button. It has most of the regular overloads. It also has overloads that support T4MVC, which is why TwitterBootstrapMVC depends on T4MVCExtensions.dll. I just love T4MVC so much that could not leave it out.
.ActionLinkButton
There is also .Bootstrap().Button() and .Bootstrap().SubmitButton() methods. The only real difference between them is that the second one has type="submit" on it. All of these three methods have the following extension methods in common:
.Bootstrap().Button()
type="submit"
.Id(string id) .Size(ButtonSize size) .Style(ButtonStyle style) .IconPrepend(Icons icon) and .IconPrepend(Icons icon, bool isWhite) .IconAppend(Icons icon) and .IconAppend(Icons icon, bool isWhite)
An example of appending an Icon is below:
@Html.Bootstrap().Button().Text("Cool button").IconAppend(Icons.camera)
In order to create button toolbars and button groups we can use disposable .Bootstrap().Container() again.
.Bootstrap().Container()
@using(Html.Bootstrap().Container().ButtonToolBar()) { using (Html.Bootstrap().Container().ButtonGroup()) { @Html.Bootstrap().Button().Text("btn1") @Html.Bootstrap().Button().Text("btn2") } using (Html.Bootstrap().Container().ButtonGroup()) { @Html.Bootstrap().Button().Text("btn3") @Html.Bootstrap().Button().Text("btn4") } }
According to Bootstrap's documentation, a drop down menu must be encapsulated into a button group. Inside it should have a button that triggers drop down event and the drop down menu itself. Lets recreate this structure with TwitterBoostrapMVC:
@using(Html.Bootstrap().Container().ButtonGroup()) { @Html.Bootstrap().Button().Text("Trigger Drop down").DropDownToggle() @Html.Bootstrap().DropDownMenu().MenuItems(menu => { menu.MenuItem(Html.ActionLink("menu item1", "action1")); menu.MenuItem(Html.ActionLink("menu item2", "action2")) .DropDownMenu().MenuItems(submenu => { submenu.MenuItem("<a href='http://www.personalitymatch.net' target='_blank'>PersonalityMatch</a>"); submenu.MenuItem(Html.ActionLink("submenu item2", "action4")); }); menu.Divider(); menu.MenuItem(Html.ActionLink("menu item3", "action3")); }) }
Each MenuItem accepts either MvcHtmlString (in order to use regular @Html.ActionLink) or regular string (In case you want to have a link to an external resource). Also each MenuItem can have a submenu as shown in example above.
MenuItem
MvcHtmlString
@Html.ActionLink
Note, I feel that implementation of Dropdown menus is already getting closer to these situations when a developer will have to come to this page to see how it needs to be used, which is why I was hesitant to implement it, but I did anyway to see what kind of feedback I get.
Bootstrap provides a way for you to add description to the input by the means of the following markup:
<span class="help-block">Example block-level help text here.</span>
I have created a helper for that as well:
@Html.Bootstrap().HelpText("text", HelpTextStyle.Inline)
This helper also exists on every basic input for you to easily attach descriptions to the inputs if needed. I've also used this mechanism to display validation error messages for the inputs. In case you do not want to display error messages next to the input, you can use the following syntax:
@Html.Bootstrap().ControlGroup().TextBoxFor(m => m.UserName).ShowValidationMessage(false)
All input helpers have another extension helpers: .Tooltip(string text) and .Popover(string title, string content). They are used to put appropriate attributes on the input in order for Bootstrap tooltip or Bootstrap popover to show up.
.Tooltip(string text)
.Popover(string title, string content)
There is one catch though. Tooltips and Popovers require a bit of JavaScript in order for it to be triggered. Include the following on the $(document).ready(...) function of your main script and you won't need to ever worry about writing any JavaScript for a given tooltip.
$(document).ready(...)
$('[rel=tooltip]').tooltip(); $('[rel=popover]').popover();
Please note that if you are loading content that contains tooltips or popovers via ajax, you will need to run this JavaScript code again.
Example of usage:
Tooltip:
@Html.Bootstrap().ControlGroup().TextBoxFor(x => x.UserName).Tooltip("This is an awesome tooltip!")
Advanced version of popover.
@Html.Bootstrap().Icon(Icons.info_sign).Popover(new PopoverConfiguration( "Info <span class='pull-right close'>X</span>","<p>This is an awesome popover!</p>") { Html = true })
Include the following JavaScript code to handle closing on the popover when "X" is clicked.
$(document).on('click', '.close', function () { $(this).closest('.popover').prev('[rel=popover]').popover('hide'); });
Consider this as a bonus. My experience shows that a developer is often asked to create a list of checkboxes or radio buttons for the website. MVC does not have html helpers to support this kind of a need. This is why I've created these helpers to address this issue.
An inspiration for these two helpers (as well as a bit of code) was another library called CheckBoxList(For) - A missing MVC extension written by Mikhail Tsennykh - a huge thanks to him!
Input Lists From IEnumerable<T>:
@Html.Bootstrap().CheckBoxListFor(m => m.CarsDriven, m => m.AllCars, car => car.Id, car => car.Name) @Html.Bootstrap().RadioButtonListFor(m => m.CarsDriven, m => m.AllCars, car => car.Id, car => car.Name)
The constructor for both of these helpers takes four required parameters:
int/string
IEnumerable<int/string>
IEnumerable<T>
<T>
The following chaining methods are available:
@Html.Bootstrap().CheckBoxListFor(...) .SelectedValues(dataSource => dataSource.Selected) // specify which checkboxes are selected .DisabledValues(dataSource => dataSource.Disabled) // specify which checkboxes are disabled .InputHtmlAttributes(dataSource => new { @class = dataSource.SomeProperty }) // customize html attributes per checkbox input .LabelHtmlAttributes(dataSource => new { @class = dataSource.SomeProperty }) // customize html attributes per checkbox label .DisplayInColumns(int numberOfColumns, int columnPixelWidth) // lets you display inputs in columns with specified width .Label() // Customize the main label for the group of checkboxes
Input Lists From Enum:
@Html.Bootstrap().RadioButtonsFromEnumFor(m => m.SomeEnum) @Html.Bootstrap().CheckBoxesFromEnumFor(m => m.SomeEnum) @Html.Bootstrap().ControlGroup().RadioButtonsFromEnumFor(m => m.SomeEnum) @Html.Bootstrap().ControlGroup().CheckBoxesFromEnumFor(m => m.SomeEnum)
.DisplayInColumns(int numberOfColumns, int columnPixelWidth) .DisplayInColumns(int numberOfColumns, int columnPixelWidth, bool condition) .DisplayInlineBlock(int marginRightPx = 0)
Under the hood they use the same rendering methods as methods .CheckBoxListFor(...) and .RadioButtonListFor(...), so the output html elements will have the same classes.
.CheckBoxListFor(...)
.RadioButtonListFor(...)
You can decorate enum values with either a [Description("cool enum")] or a [Display(Name = "cool enum")] attributes. Associated with the input label will understand any of these two.
[Description("cool enum")]
[Display(Name = "cool enum")]
True/False Inputs:
Also often I was asked to create a "Yes/No" or "True/False" type of radio button scenario for a boolean property. For this case I've written the following helper.
@Html.Bootstrap().RadioButtonTrueFalseFor(m => m.IsActivated)
The following chaining methods are available to customize the output:
.HtmlAttributesInputTrue(object htmlAttributes) // specify html attributes for the true/yes input .HtmlAttributesInputFalse(object htmlAttributes) // specify html attributes for the false/no input .HtmlAttributesLabelTrue(object htmlAttributes) // specify html attributes for the true/yes label .HtmlAttributesLabelFalse(object htmlAttributes) // specify html attributes for the false/no label .InputLabelsText(strig trueText, string falseText) // modify text for true and false labels .InputsValues(object trueValue, object falseValue) // modify values for true and false inputs
.RadioButtonsFromEnumFor(...)
.CheckBoxesFromEnumFor(...)
.LabelFor(...)
.CustomControls(...)
.ValidationMessage(...)
.ShowValidationMessage(...)
.Id(string id)