Play FrameworkのFormに独自レイアウトを適用する方法です。今回、下記のようなmaterial-design-liteを適用したログインフォームを作成したかったのです。

環境


  • Play Framework 2.6.3

ダメな例


ところが、material-design-liteのTEXT FIELDSを参考にして、下記のようにフォームヘルパーの@inputTextclassにmaterial-design-liteのmdl-textfield__inputを指定するだけだとダメなんですね。コードは下記のような感じです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@import play.api.data.Form
@import play.api.i18n.Messages
@import play.api.mvc.RequestHeader
@import controllers.AssetsFinder
@import helper._

@(signInForm: Form[app.controllers.AuthController.SignInFormData])
(implicit request: RequestHeader, messages: Messages, assets: AssetsFinder)

@main(messages("sign.in.title")) {
<div class="mdl-card mdl-shadow--2dp">
<div class="mdl-card__supporting-text">
<fieldset>
<legend>@messages("sign.in.credentials")</legend>
@form(CSRF(app.controllers.routes.AuthController.signIn)) {
<div class="mdl-textfield mdl-js-textfield">
@inputText(
signInForm("uid"),
'class -> "mdl-textfield__input" <- コレ
)
<label class="mdl-textfield__label">UserName</label>
</div>
<br/>
<div class="mdl-textfield mdl-js-textfield">
@inputPassword(
signInForm("password"),
'class -> "mdl-textfield__input" <- コレ
)
<label class="mdl-textfield__label">Password</label>
</div>
<br/>
<button class="mdl-button mdl-js-button mdl-button--primary">
@messages("sign.in")
</button>
}
</fieldset>
</div>
<div>
}

このコードだと、下記のようになってしまいます。

FieldConstractor


Form生成時にFieldConstractorというものが適用されます。
一旦、material-design-liteは置いておいて、先ほどのコードからmaterial-design-liteの部分を消すと、デフォルトだと下記のようなHTMLが生成されます。

1
2
3
4
5
6
7
<dl class=" " id="uid_field">
<dt><label for="uid">uid</label></dt>
<dd>
<input type="text" id="uid" name="uid" value="" class="mdl-textfield__input"/>
</dd>
<dd class="info">Required</dd>
</dl>

上記コードだと見た目はこんな感じです。

独自のFieldConstructor


独自のFieldConstructorを作ることで生成されるHTMLを独自に定義することができます。ので、まず、View配下にplainFieldConstructor.scala.htmlというファイルを作成して、下記のコードを書きます。

1
2
3
@(elements: helper.FieldElements)

@elements.input

これを利用するViewで設定します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@(signInForm: Form[app.controllers.AuthController.SignInFormData])
(implicit request: RequestHeader, messages: Messages, assets: AssetsFinder)

@implicitField = @{ FieldConstructor(plainFieldConstructor.f) } <-コレ

@main(messages("sign.in.title")) {
<div class="mdl-card mdl-shadow--2dp">
<div class="mdl-card__supporting-text">
<fieldset>
<legend>@messages("sign.in.credentials")</legend>
@form(CSRF(app.controllers.routes.AuthController.signIn)) {
@inputText(
signInForm("uid"),
)
<br/>
@inputPassword(
signInForm("password"),
)
<br/>
<button class="mdl-button mdl-js-button mdl-button--primary">
@messages("sign.in")
</button>
}

すると、下記のようなHTMLが生成されます。デフォルトのFieldConstructorで生成されていたlabelとかが消えて、単純なinputタグが生成されています。

1
<input type="text" id="uid" name="uid" value=""/>

見た目はこんな感じです。

CSSフレームワークの適用


最後に、material-design-liteを適用します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...

@(signInForm: Form[app.controllers.AuthController.SignInFormData])
(implicit request: RequestHeader, messages: Messages, assets: AssetsFinder)

@implicitField = @{ FieldConstructor(plainFieldConstructor.f) }

@main(messages("sign.in.title")) {
<div class="mdl-card mdl-shadow--2dp">
<div class="mdl-card__supporting-text">
<fieldset>
<legend>@messages("sign.in.credentials")</legend>
@form(CSRF(app.controllers.routes.AuthController.signIn)) {
<div class="mdl-textfield mdl-js-textfield"> <-コレとか
@inputText(
signInForm("uid"),
'class -> "mdl-textfield__input" <-コレとか
)
<label class="mdl-textfield__label">UserName</label> <-コレ
</div>
...

これで以下のように、目的のレイアウトのフォームが作成できました。