Multifield support

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.

The helper b4.multifield tries to:

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)

Here are the special arguments you can use as globalArgs (check their functionality at the docs): All of these arguments parameterize the global form-group for the set of the fields, so they will affect to every field. Thus, you can't set different validation status for different fields, for example.

Let's see a couple of examples

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.

A date range

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:

to
Select a date range from 10-11-2014
@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:

@(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).

From Play 2.4, the implicit Messages argument defined in your custom template collides with the implicit one taken from PlayMagicForJava object, and it raises a compilation error. So you only need to remove this implicit argument from your custom template.

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>

A set of checkboxes

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.

Mark what you want
Mark what you want
@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:

@(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>
}
From Play 2.4, the implicit Messages argument defined in your custom template collides with the implicit one taken from PlayMagicForJava object, and it raises a compilation error. So you only need to remove this implicit argument from your custom template.

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>

A textfield with a checkbox addon

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:

@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:

@(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>
}
From Play 2.4, the implicit Messages argument defined in your custom template collides with the implicit one taken from PlayMagicForJava object, and it raises a compilation error. So you only need to remove this implicit argument from your custom template.

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-text-checkbox {
  .form-check {
    padding: 0;
  }
  input {
    position: relative !important;
    margin: 0;
  }
}

Validation states

You can also manage the validation state for a multifield.

to
 
error!
 
The value is incorrect
@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" )