Implement your own helpers or field constructors

This library tries to be an out-of-the-box plugin. You simply need to install the library and you can start to write your forms. However, although it is a very versatile library, you may have different necessities. Thus, let's see some examples to know how you could extend it.

Create your own helper

To create a helper is very easy. You only have to look the helpers within the Github repository. Let's suppose you want to add an input group for every email input. And you would like to create your own email helper like this one:

@

So let's add a file within your views folder. For example app/views/b4/my/email.scala.html, and write the following code:

@(field: Field, args: (Symbol,Any)*)(implicit handler: b4.B4FieldConstructor, messages: Messages)
@b4.inputFormGroup(field, withFeedback = false, withLabelFor = true, b4.Args.withDefault(args, 'class -> "form-control")) { fieldInfo =>
  <div class="input-group">
    <div class="input-group-prepend">
      <span class="input-group-text">@@</span>
    </div>
    <input type="email" id="@fieldInfo.id" name="@fieldInfo.name" value="@fieldInfo.value" @toHtmlArgs(fieldInfo.innerArgsMap)>
  </div>
}

The code is a reduced version of b4.inputWrapped adapted to this purpose (you can see it here). The library is very easy and it's documented, so you can check it to understand how it works. The main concept is that b4.inputFormGroup will generate the whole form-group. For that it needs the field, some arguments and a function that returns the input HTML with the field information. All that field information is stored in a BSFieldInfo.

And that's all! All you need now is to call it from your templates:

@b4.my.email( fooForm("foo"), '_label -> "Email", 'placeholder -> "example@mail.com" )
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.

The use of b4.inputWrapped

The b4.inputWrapped is a helper that lets you to customize easily a textual input. But it is really helpful for creating your own helper. It is easier to implement than the previous example and it also lets you to add the optional feedback icons in case you need them. Now let's suppose you want create your own number helper like this one (note this example requires some extra JavaScript for the functionality and CSS):

@b4.my.number( fooForm("foo"), '_label -> "Number", 'value -> 0 )

Then we could create the file app/views/b4/my/number.scala.html with the following code:

@(field: Field, args: (Symbol,Any)*)(implicit handler: b4.B4FieldConstructor, messages: Messages)
@b4.inputWrapped("text", field, args:_*) { input =>
  <div class="input-number input-group">
    <div class="input-group-prepend">
      <span class="input-group-text input-number-minus"><i class="fa fa-minus"></i></span>
    </div>
    @input
    <div class="input-group-append">
      <span class="input-group-text input-number-plus"><i class="fa fa-plus"></i></span>
    </div>
  </div>
}

Create your multifield helper

The purpose of b4.multifield helper is to be a tool for creating multifield helpers. The method is exactly the same as the previous one for b4.inputWrapped. You can see more information and examples at the Multifield section.

Create your own field constructor

Creating new helpers is useful for adding or customizing the input structure of a field, but you may need your own field constructor, with another form-group structure. Let's see another example. Imagine that you want a vertical form but with any help information (errors, constraints and help messages) at a side panel. Something like this:

@
  • Maximum length: 20
<div class="my-form-group form-group has-danger" id="foo_field">
  <div class="field-container">
    <label for="foo">Email</label>
    <div class="input-group">
      <div class="input-group-prepend">
        <span class="input-group-text">@</span>
      </div>
      <input type="email" id="foo" name="foo" value="" aria-describedby="foo_info_0 foo_error_0" aria-invalid="true" class="form-control" placeholder="example@mail.com">
    </div>
  </div>
  <div class="alert alert-danger" role="alert">
    <ul>
      <li><span id="foo_error_0" class="help-error"><i class="fa fa-remove"></i> And error occurred!</span></li>
      <li><span id="foo_info_0" class="help-info"><i class="fa fa-warning-sign"></i> Maximum length: 10</span></li>
    </ul>
  </div>
</div>

We are going to make it easy. Let's going to copy the files for the vertical field constructor and modify them. You can see the files here. So create a folder views/b4/my/vertical and copy there the files bsFieldConstructor.scala.html, bsFormGroup.scala.html and package.scala.

Modify bsFieldConstructor.scala.html and bsFormGroup.scala.html to get the functionality you want. For this example, the first one would be re-written to something like this:

@(fieldInfo: b4.B4FieldInfo, inputHtml: Html)(implicit messages: Messages)
@alertStatus = @{
  if (fieldInfo.hasErrors)
    "alert-danger"
  else if (b4.ArgsMap.isTrue(fieldInfo.argsMap, '_success))
    "alert-success"
  else
    "alert-info"
}
<div class="my-form-group form-group @fieldInfo.argsMap.get('_class) @fieldInfo.status.map("has-"+_)" id="@fieldInfo.idFormField">
  <div class="field-container">
    @fieldInfo.labelOpt.map { label =>
      <label@if(fieldInfo.hideLabel){ class="sr-only"} @if(fieldInfo.withLabelFor){for="@fieldInfo.id"}>@bs.Args.msg(label)(messages)</label>
    }
    @inputHtml
  </div>
  <div class="alert @alertStatus" role="alert">
    <ul>
      @fieldInfo.feedbackInfos.map { case (id, text) =>
        <li><span id="@id" class="form-control-feedback">@bs.Args.msg(text)(messages)</span></li>
      }
      @fieldInfo.helpInfos.map { case (id, text) =>
        <li><span id="@id" class="form-text text-muted">@bs.Args.msg(text)(messages)</span></li>
      }
    </ul>
  </div>
</div>

Do the same with bsFormGroup.scala.html. You can check them at the Github repository here: bsFieldConstructor.scala.html and bsFormGroup.scala.html.

Finally, the modifications for this examples to package.scala are minimal:

You can check the code here.

And that's all, you now can use your own field constructor as any other with:

@import b4.my.vertical.fieldConstructor    // Declare it as default
@b4.my.email( fooForm("foo"), '_label -> "Email", '_error -> "And error occurred!", '_showConstraints -> true, 'placeholder -> "example@mail.com" )

Or even using it for specific forms:

@b4.my.vertical.form(routes.Application.extendIt) { implicit myfc =>
  @b4.my.email( fooForm("foo"), '_label -> "Email", '_error -> "And error occurred!", '_showConstraints -> true, 'placeholder -> "example@mail.com" )
}