Documentation
1.6-P28-B4  Play 2.8  Bootstrap 4 

This page explains each component in more details.

Field Constructors

Every input helper needs an implicit field constructor to be rendered. For that, you can create your own default one and import it on every template, create a new one on top of each template, or simply create a new one for each form. There are 4 different field constructors you can use.

Using form helpers

The simpler option is to use one of the specific form helpers that create for you a new implicit field constructor.

@b4.horizontal.form(routes.Application.handleRequest) { implicit hfc =>
  @b4.text( fooForm("foo"), '_label -> "Input Text" )
  ...
}

Declare it on top of your template

Another way is creating your field constructor on top of your template.

@implicitFieldConstructor = @{ b4.horizontal.fieldConstructor("col-md-2", "col-md-10") }

@b4.form(routes.Application.handleRequest) {
  @b4.text( fooForm("foo"), '_label -> "Input Text" )
  ...
}

Your default field constructor

Finally, you can create your own default field constructor in order to use it wherever you need. Within your views folder, for example in app/views/b4/myHorizontal/package.scala, create it as implicit.

package views.html.b4
package object myHorizontal {
  implicit val fieldConstructor = b4.horizontal.fieldConstructor("col-md-2", "col-md-10")
}

Now, you can use it simply importing it.

@import b4.myHorizontal.fieldConstructor    // Declare it as default

@b4.form(routes.Application.handleRequest) {
  @b4.text( fooForm("foo"), '_label -> "Input Text" )
  ...
}

Vertical field constructor @(isCustom: Boolean = false, withFeedbackTooltip: Boolean = false)

For a simple vertical field constructor:

b4.vertical.fieldConstructor()

This will let you render form-group's like this:

<div class="form-group" id="foo_field">
  <label for="foo">A simple text</label>
  <input type="text" id="foo" name="foo" value="" aria-describedby="foo_info_0" class="form-control">
  <small id="foo_info_0" class="form-text text-muted">This is a help text</small>
</div>

If you prefer to use Bootstrap 4 custom fields and feedback tooltips:

b4.vertical.fieldConstructor(isCustom = true, withFeedbackTooltip = true)

Horizontal field constructor @(colLabel: String, colInput: String, isCustom: Boolean = false, withFeedbackTooltip: Boolean = false)

You have to specify its column widths for the form-group.

b4.horizontal.fieldConstructor("col-md-2", "col-md-10")

This will let you render form-group's like this:

<div class="form-group row" id="foo_field">
  <label class="col-form-label col-md-2" for="foo">A simple text</label>
  <div class="col-md-10">
    <input type="text" id="foo" name="foo" value="" aria-describedby="foo_info_0" class="form-control">
    <small id="foo_info_0" class="form-text text-muted">This is a help text</small>
  </div>
</div>

If you prefer to use Bootstrap 4 custom fields and feedback tooltips:

b4.horizontal.fieldConstructor("col-md-2", "col-md-10", isCustom = true, withFeedbackTooltip = true)

Inline field constructor @(isCustom: Boolean = false, withFeedbackTooltip: Boolean = false)

b4.inline.fieldConstructor()

This will let you render form-group's like this:

<div class="form-group" id="foo_field">
  <label class="sr-only" for="foo">A simple text</label>
  <input type="text" id="foo" name="foo" value="" aria-describedby="foo_info_0" class="form-control">
  <small id="foo_info_0" class="form-text text-muted">This is a help text</small>
</div>

If you prefer to use Bootstrap 4 custom fields and feedback tooltips:

b4.inline.fieldConstructor(isCustom = true, withFeedbackTooltip = true)

Clear field constructor @(isCustom: Boolean = false, withFeedbackTooltip: Boolean = false)

This field constructor renders the input helpers directly. It is useful for those cases you need the input without its corresponding form-group. However, although it is used for specific cases, you can also declare it as the default field constructor within a template.

b4.clear.fieldConstructor()

If you prefer to use Bootstrap 4 custom fields and feedback tooltips:

b4.clear.fieldConstructor(isCustom = true, withFeedbackTooltip = true)

Specific field constructors

There are also 4 extra field constructors reserved to use for specify them instead of the default one. In short, these field constructors are equivalent to the previous ones but they are preferred when an implicit field constructor is needed. See the section Forms with a specific BSFieldConstructor for more details.

b4.vertical.fieldConstructorSpecific()
b4.horizontal.fieldConstructorSpecific("col-md-2", "col-md-10")
b4.inline.fieldConstructorSpecific()
b4.clear.fieldConstructorSpecific()

If you would need to specify a different BSFieldConstructor within a form, you can do:

@implicitVerticalFC = @{ b4.vertical.fieldConstructor() }
@horizontalFC = @{ b4.horizontal.fieldConstructor("col-md-2", "col-md-10") }
@b4.form(routes.Application.handleRequest) { // vertical form as default
  @b4.text( fooForm("foo"), '_label -> "Vertical input text" )
  @b4.text( fooForm("bar"), '_label -> "Horizontal input text" )(horizontalFC, implicitly[Messages])
  ...
}

Or:

@implicitVerticalFC = @{ b4.vertical.fieldConstructor() }
@b4.form(routes.Application.handleRequest) { // vertical form as default
  @b4.text( fooForm("foo"), '_label -> "Vertical input text" )
  ...
  // an inline inner section
  @defining(b4.inline.fieldConstructorSpecific()) { implicit ifc =>
    @b4.text( fooForm("bar"), '_label -> "Inline input text" )
    ...
  }
  ...
}

But remember that inline controls need to be inside their corresponding .form-inline to be rendered correctly. So you may need to wrap them with a div with these classes.

Arguments (args)

For every component there is a list of arguments you can add and they will be put within the corresponding input tag as normal HTML. However it will only happen with those arguments without a prefixed underscore (_). The underscored arguments are used to complement the corresponding form-group or to parameterize its behaviour.

For example, the following component has the argument placeholder that will be added to the input text tag. The _label argument set the label for the form-group and the _showConstraints one specifies if the constraints should be shown.

Maximum length: 10
@b4.text( fooForm("foo"), '_label -> "Constraints", '_showConstraints -> true, 'placeholder -> "A simple text showing its constraints..." )

The previous example outputs the HTML:

<div class="form-group" id="foo_field">
  <label for="foo">Constraints</label>
  <input type="text" id="foo" name="foo" value="" class="form-control" placeholder="A simple text showing its constraints...">
  <small class="form-text text-muted">Maximum length: 10</small>
</div>

Special arguments (the underscored ones)

As we see before, there are some special arguments that are reserved to parameterize the corresponding form-group's behaviour. All of these special arguments are prefixed with an underscore (_). And the list is:

  • _id: the id for the form-group. If it isn't present, the default value will be the input's id with the suffix "_field".
  • _class: the class for the form-group (the form-group class is always added).
  • _label: the text for the label. It's automatically internationalized.
  • _hideLabel: a boolean indicating if the label must be hidden. It will be rendered into the DOM, but it will add the class sr-only to the label to hide it.
  • _hiddenLabel: a shortcut to use a hidden label instead of using _label and _hideLabel. It's automatically internationalized.
  • _showConstraints: indicates if the constraints are shown.
  • _help: a help text below the input. It's automatically internationalized.
  • _success: could be:
    • Boolean: indicates if the field should show an success validation status.
    • Any: calls toString method to show a internationalized help text.
  • _warning: could be:
    • Boolean: indicates if the field should show an warning validation status.
    • Any: calls toString method to show a internationalized help text.
  • _error: could be:
    • Boolean: indicates if the field should show an error validation status.
    • Option[play.api.data.FormError]: shows the corresponding error as a help text if it's defined.
    • Any: calls toString method to show a internationalized help text.
  • _showErrors: boolean indicating if the errors should be shown. True is the default value.
About the labels

Note there are 3 arguments for rendering the label for each field: '_label, '_hideLabel and '_hiddenLabel. Screen readers will have trouble with your forms if you don't include a label for every input. So you should always add a label even if you don't want it to be displayed. That's the reason for hiding the label. Let's see when to use each case with some examples:

  • '_label: the general case.
    @b4.text( fooForm("foo"), '_label -> "Input Text", 'placeholder -> "A simple text..." )
  • '_hiddenLabel (or '_label with '_hideLabel): for that cases you don't want the label to be displayed. For example when you use inline forms.
    @implicitFC = @{ b4.inline.fieldConstructor() }
    @b4.text( fooForm("foo"), '_hiddenLabel -> "Input Text", 'placeholder -> "A simple text..." )
  • Nothing: simply omit any of these arguments for that cases you don't want to render the label even into the DOM. Use this case only if you have a reason for that. For example, you should not add any label when you don't want to display it for checkboxes or radio buttons, because they use their own inner labels for each option.
    // checkbox and radio without label
    @b4.checkbox( fooForm("foo"), '_text -> "Checkbox", 'checked -> true )
    @b4.radio( fooForm("foo"), options = Seq("M"->"Male","F"->"Female") )

Validation arguments

If a field has validation constraints they will be added automatically as HTML5 data form validation (please visit this web to learn more and check wufoo for more examples and browser compatibility).

Then, the following constraints are automatically represented in HTML with these corresponding attributes:

  • nonEmpty and nonEmptyTextrequired="true"
  • minLengthminlength="N"
  • maxLengthmaxlength="N"
  • minmin="N"
  • maxmax="N"
  • patternpattern="XXX"

Remember you have also the specific b4.email, b4.url and b4.number helpers for the corresponding validations.

For example, if you have this form

val validationForm = Form(tuple(
  "username" -> nonEmptyText(maxLength = 20),
  "email" -> email,
  "age" -> number(min = 18, max = 99),
  "color" -> nonEmptyText.verifying(pattern("^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$".r))
))

and this CSS

input:valid { color: green; }
input:invalid { color: red; }

The constraints will be automatically added as you can see here:

A username between 1 and 20 characters
From 18 to 99 years old
Format is #CCC or #CCCCCC
@b4.text( validationForm("username"), '_label -> "Username", '_help -> "A username between 1 and 20 characters" )
@b4.email( validationForm("email"), '_label -> "Email" )
@b4.number( validationForm("age"), '_label -> "Age", '_help -> "From 18 to 99 years old" )
@b4.text( validationForm("color"), '_label -> "Hexadecimal color", '_help -> "Format is #CCC or #CCCCCC" )

Optional arguments

In some cases, you may need to add an argument only if a condition happens. In this situation you can simply use an Option value and it will be omitted if it is not defined (i.e. None).

'foo -> Some("foo-value")   // foo="foo-value"
'foo -> None                // (this argument is omitted)
'foo -> maybeValue(...)     // foo="result-value"  or  it's ommited

Boolean arguments

The boolean arguments are simply that, booleans. Let's see an example for the disabled attribute. Look all of these different codes:

<input type="text" name="foo" disabled>
<input type="text" name="foo" disabled="true">
<input type="text" name="foo" disabled="false">
<input type="text" name="foo" disabled="blah">

And all of them are equivalents... But don't worry, every false argument will be automatically removed. So you can feel free to do something like:

@b4.text( fooForm("foo"), '_label -> "A maybe disabled text", 'disabled -> maybeTrueFunction(...) )

And what happens if you need to set a "false" value to an attribute? Ok, you can simply convert it to a string:

@b4.text( fooForm("foo"), '_label -> "With a false attribute", 'fooAttr -> "false" )
@b4.text( fooForm("foo"), '_label -> "With a boolean attribute", 'fooAttr -> maybeTrueFunction(...).toString )

Note only regular arguments with false value are removed. It doesn't happen with the special ones (i.e. the underscored ones).

Arguments with dashes (e.g. data-* attributes)

You always add any argument to helpers using tuples of (Symbol, Any), like 'class -> "foo-class". However, when you need to add an argument with dashes you cannot declare the symbol as 'data-attr. So to get this you have different solutions:

  • Create the Symbol directly with its apply method: Symbol("data-attr") -> "data-value".
  • Import the implicit views.html.helper.Implicits.toAttributePair with simply @import helper.Implicits._. And then you will be able to declare the argument as "data-attr" -> "data-value".

ARIA attributes

The WAI-ARIA attributes are a very helpful tool for accesibility. However we know it could be difficult and boring to integrate it everywhere within your web. Because of that, the library will do automatically for you for any field constructor.

@b4.text( fooForm("text"), '_label -> "A text", '_help -> "Something helpful" )

And it will render the following if there is an error:

<div class="form-group has-danger" id="text_field">
  <label for="text">A text</label>
  <input type="text" id="text" name="text" value="" aria-describedby="text_error_0 text_info_0" aria-invalid="true" class="form-control">
  <div id="text_error_0" class="form-control-feedback">This field is required</div>
  <small id="text_info_0" class="form-text text-muted">Something helpful</small>
</div>

Note the library adds automatically for you:

  • A span for each error with an autogenerated id attribute.
  • A span for each extra information (i.e. a help block added with a '_help argument or any constraint if '_showConstraints is active) with an autogenerated id attribute.
  • The aria-describedby attribute for the input with the list of every span it has added.
  • If there is any error, it is also added he aria-invalid attribute for the input setted to true.

Forms @(action: Call, args: (Symbol, Any)*)(body: => Html)(implicit fc: BSFieldConstructor)

The b4.form takes a Call object and a list of arguments. The implicit BSFieldConstructor argument is used to know which class is needed. Each BSFieldConstructor adds its corresponding class:

  • InlineFieldConstructor: class="form-inline"
  • HorizontalFieldConstructor: class="form-horizontal"
  • VerticalFieldConstructor: class="form-vertical"
  • ClearFieldConstructor: class="form-clear"

The last 3 ones don't belong to Bootstrap 4, but they could be useful for your own CSS. Every other class you include within args will be appended to this required one.

If you would want to remove theses default classes, you can add the '_disableDefaultClass -> true argument. This is useful for example if you have an inline form with some layout containers or custom wrappers for your form-groups, due to "form-inline" class will create a flex row layout.

The helper also adds the role="form" attribute by default.

Forms with a specific BSFieldConstructor

Every input helper will use the default BSFieldConstructor (the implicit one), unless you specify another. To do this you can use the form helper. There is one for each type of form. Here is an example of how to add an inline form within a view with a vertical form as default.

@implicitVerticalFC = @{ b4.vertical.fieldConstructor() }
@b4.form(routes.Application.handleRequest) { // vertical form as default
  @b4.text( fooForm("foo"), '_label -> "Input Text" )
  ...
}
@b4.inline.form(routes.Application.handleRequest) { implicit ifc => // an inline form
  @b4.text( fooForm("foo"), '_label -> "Input Text" )
  ...
}

There are 4 different options to insert each type of form. Note that there is also one for a clear BSFieldConstructor (it simply outputs the input helper without any more). So we have:

@b4.vertical.form(routes.Application.handleRequest) { implicit vfc => ... }
@b4.horizontal.form(routes.Application.handleRequest, "col-md-2", "col-md-10") { implicit hfc => ... }
@b4.inline.form(routes.Application.handleRequest) { implicit ifc => ... }
@b4.clear.form(routes.Application.handleRequest) { implicit cfc => ... }

Forms with B4 custom fields and feedback tooltips

As we saw, a BSFieldConstructor can be parameterize for using custom fields or feedback tooltips. You can also declare them directly with these specific forms using the arguments '_custom and '_feedbackTooltip.

@b4.vertical.form(routes.Application.handleRequest, '_custom -> true, '_feedbackTooltip -> true) { implicit vfc => ... }

Forms with CSRF token

If you want to add automatically a CSRF token to a form, you can do it simply using the equivalent formCSRF. Be sure you implement your project correctly for CSRF support as it is explained at the Play's documentation.

@b4.formCSRF(...) { ... }
@b4.vertical.formCSRF(...) { implicit vfc => ... }
@b4.horizontal.formCSRF(...) { implicit hfc => ... }
@b4.inline.formCSRF(...) { implicit ifc => ... }
@b4.clear.formCSRF(...) { implicit cfc => ... }

It will simply add the helper @helper.CSRF.formField within the form for you.

Input helpers

About disabled and readonly attributes

Both the disabled and readonly attributes for an input prevent the user from modify it. However, the disabled attribute means this input will NOT be sent within the POST request, whereas the readonly one means it will.

Nevertheless, for checkbox, radio and select tags it doesn't happen. To support the readonly attribute for these tags, the corresponding helpers have been adapted to behave as would be expected. To do that, when the readonly attribute appears the helper will:

  • add an additional disabled attribute
  • add an additional <input type="hidden"> with the desired value
  • wraps them with in a div (with class checkbox-group, radio-group or select-group)
Note it only happens when the readonly attribute is present, even with a false value. It is done to make it easier to modify its readonly behaviour using javascript.

You can see an example in the Readonly Demo.

Validation states & feedback tooltips

You can set any validation state for a field with one of the following special arguments:

  • _success: could be:
    • Boolean: indicates if the field should show an success validation status.
    • Any: calls toString method to show a internationalized help text.
  • _warning: could be:
    • Boolean: indicates if the field should show an warning validation status.
    • Any: calls toString method to show a internationalized help text.
  • _error: could be:
    • Boolean: indicates if the field should show an error validation status.
    • Option[play.api.data.FormError]: shows the corresponding error as a help text if it's defined.
    • Any: calls toString method to show a internationalized help text.

Note! warning state is no more supported by Bootstrap 4, so if you use it you would need to add your own styling.

Great!
A help text
Be carefull with this...
A help text
An error occurred!
A help text
@b4.text( fooForm("foo"), '_label -> "Success", '_success -> "Great!", '_help -> "A help text", 'placeholder -> "Success text..." )
@b4.text( fooForm("foo"), '_label -> "Warning", '_warning -> "Be carefull with this...", '_help -> "A help text", 'placeholder -> "Warning text..." )
@b4.text( fooForm("foo"), '_label -> "Error", '_error -> "An error occurred!", '_help -> "A help text", 'placeholder -> "Error text..." )

If you prefer to use tooltips for your validations, you only need to parameterize the wrapping field constructor. For example, using the form helper:

Great!
Be carefull with this...
An error occurred!
Another help text
@b4.vertical.form(routes.Application.vertical, '_feedbackTooltip -> true) { implicit vfc =>
  @b4.text( fooForm("foo"), '_label -> "Success", '_showIconValid -> true, '_class -> "position-relative", 'placeholder -> "Success text..." )
  @b4.text( fooForm("foo"), '_label -> "Warning", '_warning -> "Be carefull with this...", '_showIconWarning -> true, '_class -> "position-relative", 'placeholder -> "Warning text..." )
  @b4.text( fooForm("foo"), '_label -> "Error", '_error -> "An error occurred!", '_showIconOnError -> true, '_help -> "Another help text", '_class -> "position-relative", 'placeholder -> "Error text..." )
}

b4.inputType @(inputType: String, field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It renders a simple input with a specific type attribute and it adds class="form-control" by default, but you can add an extra class with 'class -> "extra_class".

The argument 'placeholder is automatically internationalized.

With at least 8 characters
@b4.inputType( "text", fooForm("name"), 'class -> "extra_class", '_label -> "Name", 'placeholder -> "John Doe" )
@b4.inputType( "email", fooForm("email"), '_label -> "Email", 'placeholder -> "example@mail.com" )
@b4.inputType( "password", fooForm("password"), '_label -> "Password", '_help -> "With at least 8 characters" )

b4.text @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="text".

@b4.text( fooForm("name"), '_label -> "Name", 'placeholder -> "John Doe" )

b4.password @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="password".

For security reasons, this helper doesn't display the possible value the field could have (for example when the form is reloaded after a validation failure).

With at least 8 characters
@b4.password( fooForm("password"), '_label -> "Password", '_help -> "With at least 8 characters" )

b4.file @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="file".

@b4.file( fooForm("file"), '_label -> "File" )
@b4.file( fooForm("file"), '_label -> "File", 'class -> "form-control" )
Bootstrap 4 custom file

For new Bootstrap 4 custom file, you only need to add '_custom -> true. Remember that you can also parameterize the wrapping form constructor in order every helper will behave as "custom", or simply adding '_custom -> true to a form.

Additionally, you can use the argument 'placeholder.

@b4.file( fooForm("file"), '_label -> "File", '_custom -> true, 'placeholder -> "Select a file" )

b4.textarea @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It renders a textarea and it adds class="form-control" by default.

@b4.textarea( fooForm("foo"), '_label -> "Textarea", 'rows -> 3 )

b4.checkbox @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It renders a checkbox. It has the attribute value set to true by default, but you can use another one using the 'value argument.

The special '_text argument lets you put a text after the checkbox. It is automatically internationalized.

Regarding to the checked attribute, if you want to set it to true as default, use '_default -> true. And if you need to force its value use directly the 'checked argument.

It supports readonly attribute adding an additional disabled one and a <input type="hidden">.

@b4.checkbox( fooForm("foo"), '_text -> "Remember me" )
// uses "bar" as value for the checkbox
@b4.checkbox( fooForm("foo"), '_text -> "Remember me", 'value -> "bar" )
// checked by default (if the form is filled, this value will be taken)
@b4.checkbox( fooForm("foo"), '_text -> "Remember me", '_default -> true )

// always checked (even if the form has been filled with a false)
@b4.checkbox( fooForm("foo"), '_text -> "Remember me", 'checked -> true )
// disabled -> it will NOT be sent within the POST request
@b4.checkbox( fooForm("foo"), '_text -> "Remember me", 'disabled -> true )
// readonly -> it will be sent within the POST request
@b4.checkbox( fooForm("foo"), '_text -> "Remember me", 'readonly -> true )

Note you can use any value for the _text argument.

@b4.checkbox( fooForm("foo"), '_text -> Html("Do you want a beer? <i class=\"fa fa-beer\"></i>") )
Bootstrap 4 custom checkbox

For new Bootstrap 4 custom checkbox, you only need to add '_custom -> true. Remember that you can also parameterize the wrapping form constructor in order every helper will behave as "custom", or simply adding '_custom -> true to a form.

@b4.checkbox( fooForm("foo"), '_custom -> true, '_text -> Html("Do you want a beer? <i class=\"fa fa-beer\"></i>") )

b4.radio @(field: Field, options: Seq[(String, Any)], args: (Symbol, Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It renders a radio. It supports readonly attribute adding an additional disabled one and a <input type="hidden">.

It has an additional special _inline argument to make it an inline radio (for vertical and horizontal forms).

@opts = @{ Seq("M"->"Male","F"->"Female") }
@b4.radio( fooForm("foo"), options = opts, '_label -> "Radio Group" )
// an inline radio within a vertical or horizontal form
@b4.radio( fooForm("foo"), options = opts, '_label -> "Radio Group", '_inline -> true )
// with value "F" by default (if the form is filled, this value will be taken)
@b4.radio( fooForm("foo"), options = opts, '_label -> "Radio Group", 'value -> "F" )
// disabled -> it will NOT be sent within the POST request
@b4.radio( fooForm("foo"), options = opts, '_label -> "Radio Group", 'disabled -> true )
// readonly -> it will be sent within the POST request
@b4.radio( fooForm("foo"), options = opts, '_label -> "Radio Group", 'readonly -> true )

Note you can use any value for the options argument. But they are automatically internationalized only for _String_ values.

@b4.radio( fooForm("foo"), options = Seq("B" -> Html("Beer <i class=\"fa fa-beer\"></i>"), "C" -> Html("Coffee <i class=\"fa fa-coffee\"></i>")), '_label -> "What do you prefer?" )
Bootstrap 4 custom radio

For new Bootstrap 4 custom radio, you only need to add '_custom -> true. Remember that you can also parameterize the wrapping form constructor in order every helper will behave as "custom", or simply adding '_custom -> true to a form.

@b4.radio( fooForm("foo"), options = Seq("B" -> Html("""Beer <i class="fa fa-beer"></i>"""), "C" -> Html("""Coffee <i class="fa fa-coffee"></i>""")), '_custom -> true, '_label -> "What do you prefer?" )
b4.radio @(field: Field, args: (Symbol, Any)*)(content: Tuple6[Boolean, Boolean, String, String, Option[String], Map[Symbol, Any]] => Html)(implicit handler: BSFieldConstructor, messages: Messages)
b4.radioOption @(inputValue: Any, text: Any, args: (Symbol, Any)*)(implicit extraInfo: (Boolean, Boolean, String, String, Option[String], Map[Symbol,Any]), messages: Messages)

If you need more versatility you can fully customize your radio options:

@b4.radio( fooForm("foo3"), '_label -> "Ok, now that we're alone, what do you really prefer?" ) { implicit extraInfo =>
  @b4.radioOption("B", Html("Beer <i class=\"fa fa-beer\"></i>"))
  @b4.radioOption("B", Html("Coffee <i class=\"fa fa-coffee\"></i>"), 'disabled -> true)
}

Note you can use any value for the text argument. But it is automatically internationalized only for _String_ values.

b4.select @(field: Field, options: Seq[(String,String)], args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It renders a select. It supports readonly attribute adding an additional disabled one and a <input type="hidden">.

Note the value for each option argument is automatically internationalized.

@fruits = @{ Seq("A"->"Apples","P"->"Pears","B"->"Bananas") }
@b4.select( fooForm("foo"), options = fruits, '_label -> "Select" )
// disabled -> it will NOT be sent within the POST request
@b4.select( fooForm("foo"), options = fruits, '_label -> "Select", 'disabled -> true )
// readonly -> it will be sent within the POST request
@b4.select( fooForm("foo"), options = fruits, '_label -> "Select", 'readonly -> true )

You can add a default first value using the argument '_default with a string. It will add a first option to the select with an empty string value. So you could add a required constraint to the field to force the user to select one other option. In addition, this default option is always disable to avoid the user to select it, and if any other option is selected this default one will not appear at all. Note it's automatically internationalized.

@b4.select( fooForm("foo"), options = fruits, '_label -> "Select", '_default -> "Select an option" )
@b4.select( fooForm("foo"), options = fruits, '_label -> "With default and Pears as value", '_default -> "Select an option", 'value -> "P" )

For a multiple select you only need to add the 'multiple argument. In that case, the '_default argument will be ignored.

@fruits = @{ Seq("A"->"Apples","P"->"Pears","B"->"Bananas") }
@b4.select( fooForm("foo"), options = fruits, '_label -> "Select Multiple", 'multiple -> true )
// with value "A" and "B" by default
// it is a string with every value separated by commas
@b4.select( fooForm("foo"), options = fruits, '_label -> "Select Multiple", 'multiple -> true, 'value -> "A,B" )
Bootstrap 4 custom select

For new Bootstrap 4 custom select, you only need to add '_custom -> true. Remember that you can also parameterize the wrapping form constructor in order every helper will behave as "custom", or simply adding '_custom -> true to a form.

@b4.select( fooForm("foo"), options = fruits, '_custom -> true, '_label -> "Custom Select" )
b4.select @(field: Field, args: (Symbol,Any)*)(content: Set[String] => Html)(implicit handler: BSFieldConstructor, messages: Messages)
b4.selectOption @(value: Any, name: Any, args: (Symbol, Any)*)(implicit values: Set[String], messages: Messages)

If you need more versatility you can fully customize your select options:

@b4.select( fooForm("foo"), '_label -> "Grouped select" ) { implicit values =>
  <optgroup label="Group 1">
    @b4.selectOption("opt_1-1", "Option 1.1")
    @b4.selectOption("opt_1-2", "Option 1.2", 'disabled -> true)
    @b4.selectOption("opt_1-3", "Option 1.3")
  </optgroup>
  <optgroup label="Group 2">
    @b4.selectOption("opt_2-1", "Option 2.1", 'disabled -> true)
    @b4.selectOption("opt_2-2", "Option 2.2")
  </optgroup>
}
class Fruit (id: Long, name: String, isCitrus: Boolean)
@fruitsWithCitrus = @{ Seq(
  Fruit(1L, "Apple", false),
  Fruit(2L, "Orange", true),
  Fruit(3L, "Pear", false),
  Fruit(4L, "Lemon", true),
  Fruit(5L, "Banana", false)
)}
@b4.select( fooForm("foo"), '_label -> "Fruits select (lemon preselected)" ) { implicit values =>
  <optgroup label="Citrus fruits">
    @fruitsWithCitrus.filter(_.isCitrus).map { citrus =>
      @b4.selectOption(citrus.id, citrus.name, 'selected -> (citrus.name == "Lemon"))
    }
  </optgroup>
  <optgroup label="Rest of fruits">
    @fruitsWithCitrus.filterNot(_.isCitrus).map { fruit =>
      @b4.selectOption(fruit.id, fruit.name)
    }
  </optgroup>
}

Note the value for the name is automatically internationalized.

b4.hidden @(name: String, value: Any, args: (Symbol, Any)*)

It simply renders a hidden input.

@b4.hidden("fooName", "fooValue", 'fooAttr -> "fooAttrValue")

Renders to:

<input type="hidden" name="fooName" value="fooValue" fooAttr="fooAttrValue">
b4.hidden @(field: Field, args: (Symbol, Any)*)

You can also render a hidden input directly with a Field as you could do with a b4.text helper, for example. It lets you be able to simply change a b4.text for a b4.hidden to hide it.

It will automatically take the name of the Field and it's value if present. You can set a default value using the argument 'value. Finally, take into account that every "underscored" argument (with "_" as preffix) will be removed.

@b4.hidden( fooForm("name"), '_label -> "Name", 'value -> "defaultValue", 'placeholder -> "John Doe" )

Renders to:

<input type="hidden" name="name" value="fieldValue_or_defaultValue" placeholder="John Doe">

b4.hiddens @(namesAndValues: (Any, Any)*)

Render a list of hidden inputs.

@b4.hiddens("fooId" -> 1L, "barId" -> 2L)

Renders to:

<input type="hidden" name="fooId" value="1">
<input type="hidden" name="barId" value="2">

New HTML5 helpers

Important note: the new HTML5 input types are not fully supported for web browsers. Those not supported by old web browsers, will behave as input type text.

b4.color @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="color".

@b4.color( fooForm("color"), '_label -> "Color", 'value -> "#3264c8" )

b4.date @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="date".

@b4.date( fooForm("date"), '_label -> "Date" )

b4.datetime @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="datetime".

@b4.datetime( fooForm("datetime"), '_label -> "Datetime" )

b4.datetimeLocal @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="datetime-local".

@b4.datetimeLocal( fooForm("datetimeLocal"), '_label -> "Datetime-Local" )

b4.email @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="email".

@b4.email( fooForm("email"), '_label -> "Email", 'placeholder -> "example@mail.com" )

b4.month @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="month".

@b4.month( fooForm("month"), '_label -> "Month" )

b4.number @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="number".

@b4.number( fooForm("number"), '_label -> "Number", 'min -> 0, 'max -> 50, 'step -> 5 )

b4.range @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="range".

@b4.range( fooForm("range"), '_label -> "Range", 'min -> 0, 'max -> 50 )

It is a short version of b4.inputType for type="search".

@b4.search( fooForm("search"), '_label -> "Search" )

b4.tel @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="tel".

@b4.tel( fooForm("tel"), '_label -> "Telephone" )

b4.time @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="time".

@b4.time( fooForm("time"), '_label -> "Time" )

b4.url @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="url".

@b4.url( fooForm("url"), '_label -> "URL" )

b4.week @(field: Field, args: (Symbol,Any)*)(implicit handler: BSFieldConstructor, messages: Messages)

It is a short version of b4.inputType for type="week".

@b4.week( fooForm("week"), '_label -> "Week" )

More helpers

The following helpers use an auxiliar helper that creates a form-group without a field. Here are the special arguments they use:

  • _id: the id for the form-group.
  • _class: the class for the form-group (the form-group class is always added).
  • _label: the text for the label.
  • _help: a help text below the input.

b4.static @(label: String, args: (Symbol,Any)*)(text: => Html)(implicit fc: BSFieldConstructor, messages: Messages)

It renders a static control to place within your form. It takes a HTML as parameter, so you can render whatever you want. Actually, it is like a wrapper for a static HTML.

This is a link

@b4.static("Static HTML"){ <a href="#"><i class="fa fa-star"></i> This is a link</a> }

There are two equivalent more versions of b4.static helper: one for labels with HTML, and another one to omit the label. Note that you can also have a hidden label using '_hideLabel' argument.

@b4.static(Html("Label with icon <i class=\"fa fa-beer\"></i>")){ <a href="#">Basic control with label</a> }
@b4.static("Hidden label", '_hideLabel -> true){ <a href="#">Basic control with hidden label</a> }
@b4.static(){ <a href="#">Basic control without label</a> }

b4.buttonType @(buttonType: String, args: (Symbol,Any)*)(text: => Html)(implicit fc: b4.B4FieldConstructor, messages: Messages)

It renders a simple button to place within your form. It takes a HTML as parameter, so you can render whatever you want.

@b4.buttonType("submit", 'class -> "btn btn-secondary"){ <i class="fa fa-ok"></i> Sign in }

b4.submit @(args: (Symbol,Any)*)(text: => Html)(implicit fc: BSFieldConstructor, messages: Messages)

It is a short version of b4.buttonType for type="submit".

@b4.submit('class -> "btn btn-primary"){ <i class="fa fa-ok"></i> Sign in }

b4.reset @(args: (Symbol,Any)*)(text: => Html)(implicit fc: BSFieldConstructor, messages: Messages)

It is a short version of b4.buttonType for type="reset".

@b4.reset('class -> "btn btn-danger"){ <i class="fa fa-remove"></i> Reset }

b4.button @(args: (Symbol,Any)*)(text: => Html)(implicit fc: BSFieldConstructor, messages: Messages)

It is a short version of b4.buttonType for type="button".

@b4.button('class -> "btn btn-secondary"){ <i class="fa fa-heart"></i> Favorite }

b4.free @(args: (Symbol,Any)*)(content: => Html)(implicit fc: BSFieldConstructor, messages: Messages)

It renders whatever you want within a form-group div.

Cancel
@b4.free('_id -> "idFormGroup") {
  <button type="submit" class="btn btn-primary"> <i class="fa fa-ok"></i> Save changes</button>
  <a class="btn btn-secondary" href> <i class="fa fa-remove"></i> Cancel</a>
}

Your own custom helpers

The purpose of these helpers is to be a tool for creating your own helpers. Please see more about creating new helpers here.

b4.inputWrapped @(inputType: String, field: Field, args: (Symbol,Any)*)(inputGroup: Html => Html)(implicit handler: BSFieldConstructor, messages: Messages)

This is the same as b4.inputType but specifying a custom wrapper for the input tag. It is useful for input groups and inputs with validation states and feedback icons.

@b4.inputWrapped( "email", fooForm("email"), '_label -> "Email", 'placeholder -> "example@mail.com" ) { input =>
  <div class="input-group">
    <div class="input-group-prepend">
      <span class="input-group-text">@@</span>
    </div>
    @input
    <div class="input-group-append">
      <button type="button" class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown">Action</button>
      <div class="dropdown-menu" role="menu">
        <a class="dropdown-item" href="#">Action</a>
        <a class="dropdown-item" href="#">Another action</a>
        <a class="dropdown-item" href="#">Something else here</a>
        <div role="separator" class="dropdown-divider"></div>
        <a class="dropdown-item" href="#">Separated link</a>
      </div>
    </div>
  </div>
}

b4.multifield @(fields: Field*)(args: (Symbol,Any)*)(inputsHtml: (BSFieldConstructor, Messages) => Html)(implicit fc: BSFieldConstructor, messages: Messages)

Sometimes you may need two or more fields within the same line in a horizontal or vertical form (for a set of checkboxes, a date range, ...).

The helper b4.multifield tries to:

  • Manage a set of fields and group them within the same form-group.
  • Manage all of their validation texts, help texts and constraints at the end of the form-group as if they were only one.
  • Take advantage of the helpers that are already implemented.
  • Be a tool to create your custom multifield helpers.

To know how it works and how to use it go the the Multifield section.