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, ...). That is the purpose of the helper b4.multifield.
b4.multifield
The helper b4.multifield tries to:
form-group
To see how it works take a look to the arguments it takes:
@(fields: Field*)(globalArgs: Seq[(Symbol,Any)], fieldsArgs: Seq[(Symbol,Any)])(inputsHtml: ClearFieldConstructor => Html)(implicit fc: B4FieldConstructor, messages: Messages)
play.api.data.Field
_id
_class
_label
_showConstraints
_help
globalArgs
_success
_warning
_error
Remember the code of this web with these examples is available on Github. To see more details about creating your own helpers you can check before the Extend it section.
We are going to implement our own custom helper to use a date range with the bootstrap-datepicker plugin. Then, we need a helper with 2 fields: dateStart and dateEnd. This is what we want to get:
dateStart
dateEnd
@b4.datepicker( fooForm("dateStart"), 'value -> "31-10-2014" )( fooForm("dateEnd"), 'value -> "31-12-2014" )( '_label -> "Date range", "data-date-start-date" -> "01-01-2014", '_help -> "Select a date range from 10-11-2014" )
As you can see we have 2 fields with their own specific arguments for their corresponding inputs, and then a list of general arguments to decorate the form-group.
Seeing the plugin, we need to generate a code like this one within the form-group:
<div class="input-daterange input-group" id="datepicker"> <input type="text" class="form-control" name="start" /> <div class="input-group-prepend input-group-append"> <span class="input-group-text">to</span> </div> <input type="text" class="form-control" name="end" /> </div>
Then, to implement our own b4.datepicker helper we would only need to create a the file b4/datepicker.scala.html within our views folder with the following code:
b4.datepicker
b4/datepicker.scala.html
views
@(startField: Field, startArgs: (Symbol,Any)*)(endField: Field, endArgs: (Symbol,Any)*)(globalArgs: (Symbol,Any)*)(implicit fc: b4.B4FieldConstructor, messages: Messages) @b4.multifield(startField, endField)(globalArgs, startArgs ++ endArgs) { implicit cfc => <div class="input-daterange input-group" @toHtmlArgs(b4.Args.inner(globalArgs).toMap)> @b4.text(startField, startArgs:_*) <div class="input-group-prepend input-group-append"> <span class="input-group-text">to</span> </div> @b4.text(endField, endArgs:_*) </div> }
toHtmlArgs() function helps you to render a list of arguments from a Map of arguments, and b4.Args.inner() filters the underscored arguments used for decorating the form-group ( _id, _class, _label, _showConstraints and _help).
toHtmlArgs()
Map
b4.Args.inner()
Messages
PlayMagicForJava
So for the b4.datepicker used before we get rendered the following code (for a horizontal form):
<div class="form-group row" id=""> <label class="col-form-label col-md-2">Date range</label> <div class="col-md-10"> <div class="input-daterange input-group" data-date-start-date="10-11-2014"> <input type="text" id="dateStart" name="dateStart" value="15-11-2014" class="form-control"> <div class="input-group-prepend input-group-append"> <span class="input-group-text">to</span> </div> <input type="text" id="dateEnd" name="dateEnd" value="31-12-2014" class="form-control"> </div> <small class="form-text text-muted">Select a date range from 10-11-2014</small> </div> </div>
This is a classic one. A set of checkboxes within the same form-group. Note you can choose to display them vertically or inline simply by using CSS.
@b4.multiCheckbox( (fooForm("foo"), Seq('_text -> "Foo")), (fooForm("bar"), Seq('_text -> "Bar")), (fooForm("beer"), Seq('_text -> "Beer")) )('_label -> "Checkboxes", 'class -> "multi-checkbox-list", '_help -> "Mark what you want") @b4.multiCheckbox( (fooForm("foo"), Seq('_text -> "Foo")), (fooForm("bar"), Seq('_text -> "Bar")), (fooForm("beer"), Seq('_text -> "Beer")) )('_label -> "Inline", 'class -> "multi-checkbox-list inline", '_help -> "Mark what you want")
In this case we have a list of fields with their corresponding Seq of arguments and then a list of general arguments to decorate the form-group.
Then, to implement our own b4.multiCheckbox helper we would only need to create a the file b4/multiCheckbox.scala.html within our views folder with the following code:
b4.multiCheckbox
b4/multiCheckbox.scala.html
@(fieldsWithArgs: (Field, Seq[(Symbol,Any)])*)(globalArgs: (Symbol,Any)*)(implicit fc: b4.B4FieldConstructor, messages: Messages) @b4.multifield(fieldsWithArgs.map(_._1):_*)(globalArgs, fieldsWithArgs.map(_._2).flatten) { implicit cfc => <div @toHtmlArgs(b4.Args.inner(globalArgs).toMap)> @fieldsWithArgs.map { case (field, fieldArgs) => @b4.checkbox(field, fieldArgs:_*) } </div> }
So for the example:
@b4.multiCheckbox( (fooForm("foo"), Map('_text -> "Foo")), (fooForm("bar"), Map('_text -> "Bar")), (fooForm("beer"), Map('_text -> "Beer")) )('_label -> "Checkboxes", 'class -> "multi-checkbox-list", '_help -> "Mark what you want")
We will get the following HTML code (for a horizontal form):
<div class="form-group row" id=""> <label class="col-form-label col-md-2">Checkboxes</label> <div class="col-md-10"> <div class="multi-checkbox-list"> <div class="form-check"> <input type="checkbox" id="foo" name="foo" value="true" class="form-check-input"> <label class="form-check-label" for="foo"> Foo </label> </div> <div class="form-check"> <input type="checkbox" id="bar" name="bar" value="true" class="form-check-input"> <label class="form-check-label" for="bar"> Bar </label> </div> <div class="form-check"> <input type="checkbox" id="beer" name="beer" value="true" class="form-check-input"> <label class="form-check-label" for="beer"> Beer </label> </div> </div> <small class="form-text text-muted">Mark what you want</small> </div> </div>
In this example we have a checkbox within an input group's addon instead of text. Then, we need a helper with 2 fields: foo and fooSelected. This is what we want to get:
foo
fooSelected
@b4.textWithCheckbox( fooForm("foo"), 'placeholder -> "a foo value" )( fooForm("fooSelected") )('_label -> "New task" )
Then, to implement our own b4.textWithCheckbox helper we would only need to create a the file b4/textWithCheckbox.scala.html within our views folder with the following code:
b4.textWithCheckbox
b4/textWithCheckbox.scala.html
@(textField: Field, textArgs: (Symbol,Any)*)(checkboxField: Field, checkboxArgs: (Symbol,Any)*)(globalArgs: (Symbol,Any)*)(implicit fc: b4.B4FieldConstructor, messages: Messages) @b4.multifield(textField, checkboxField)(globalArgs, textArgs ++ checkboxArgs) { implicit cfc => <div class="input-text-checkbox input-group" @toHtmlArgs(b4.Args.inner(globalArgs).toMap)> <div class="input-group-prepend"> <div class="input-group-text"> @b4.checkbox(checkboxField, checkboxArgs:_*) </div> </div> @b4.text(textField, bs.Args.withDefault(textArgs, Symbol("aria-describedby") -> "checkbox-addon"):_*) </div> }
So for the b4.textWithCheckbox used before we get rendered the following code (for a horizontal form):
<div class="form-group row" id=""> <label class="col-form-label col-md-2">New task to do</label> <div class="col-md-10"> <div class="input-text-checkbox input-group"> <div class="input-group-prepend"> <div class="input-group-text"> <div class="form-check"> <input type="checkbox" id="fooSelected" name="fooSelected" value="true" class="form-check-input"> </div> </div> </div> <input type="text" id="foo" name="foo" value="" class="form-control" aria-describedby="checkbox-addon" placeholder="a foo value"> </div> </div> </div>
For this helper we need also to fix a bit the CSS manually. In the Boostrap example, the checkbox is direcly within the .input-group-addon. But I have prefered to use the default b4.checkbox helper to implement the multifield easily.
.input-group-addon
b4.checkbox
.input-text-checkbox { .form-check { padding: 0; } input { position: relative !important; margin: 0; } }
You can also manage the validation state for a multifield.
@b4.datepicker( fooForm("dateStart"), 'value -> "15-11-2014", '_showIconWarning -> true )( fooForm("dateEnd"), 'value -> "31-10-2014", '_showIconWarning -> true )( '_label -> "Date range" ) @b4.textWithCheckbox( fooForm("foo"), 'value -> "an incorrect value", '_error -> "The value is incorrect", '_showIconOnError -> true )( fooForm("fooSelected") )( '_label -> "New task" )