SkinnyFrameworkでデザイン変更しやすいフォームを実装する方法

SkinnyFrameworkにてscaffoldで生成される入力フォームを、編集しやすく改造する方法を紹介します。

大体の入力フォームでは、入力部分はinputだったりselectだったりと違ってくると思いますが、その入力欄を囲う部分は同じ内容の繰り返しになると思います。
そこで、以下では入力欄を囲う部分を共通化する２つの方法を紹介します。


### 共通部分をメソッド化する

１つ目の方法は、フィールド部分のレイアウトをメソッドにします。

```ssp:members/_form.html.ssp
<% def formField(model: String, field: String)(body: => Unit) = unescape(capture { %>
<div class='form-group ${if(keyAndErrorMessages.hasErrors(field)) "has-error" else ""}'>
  <label class="control-label" for="${field}">
    ${s.i18n.get(model + "." + skinny.util.StringUtil.toCamelCase(field))}
  </label>
  ${unescape(capture(body))}
  <div class="help-block">
    #for (error <- keyAndErrorMessages.getErrors(field))
      <div>${error}</div>
    #end
  </div>
</div>
<% }) %>

<%= formField("member", "name_text") { %>
  <input type="text" id="name_text" name="name_text" class="form-control" value="${s.params.name_text}" />
<% }  %>

<%= formField("member", "birthday_date") { %>
  <div class="row">
    <div class="col-xs-2">
      <input type="text" name="birthday_date_year"  class="form-control" value="${s.params.birthday_date_year}"  placeholder='${s.i18n.get("year")}'  maxlength=4 />
    </div>
    <div class="col-xs-2">
      <input type="text" name="birthday_date_month" class="form-control" value="${s.params.birthday_date_month}" placeholder='${s.i18n.get("month")}' maxlength=2 />
    </div>
    <div class="col-xs-2">
      <input type="text" name="birthday_date_day"   class="form-control" value="${s.params.birthday_date_day}"   placeholder='${s.i18n.get("day")}'   maxlength=2 />
    </div>
  </div>
<% }  %>
```


### 共通パーツをレイアウトファイルにする

２つ目は、共通パーツとする部分を別のsspファイルにして、layoutメソッドで読み込む方法です。
layoutメソッドは、#do〜#endまでの部分をレイアウト側で「body」という変数で取得できます。
また、withAttributesメソッドを使うことで、全体的に共通なパラメータを設定しておくことができて便利です。

この方法では、複数のフォームで_formField.html.sspを流用できるので、アプリ全体でレイアウトと統一できます。

```ssp:tags/_formField.html.ssp
<% import skinny.util.StringUtil.toCamelCase %>
<%@val s: skinny.Skinny %>
<%@val keyAndErrorMessages: skinny.KeyAndErrorMessages %>
<%@val model: String %>
<%@val field: String %>
<%@val body: String %>

<div class='form-group ${if(keyAndErrorMessages.hasErrors(field)) "has-error" else ""}'>
  <label class="control-label" for="${field}">
    ${s.i18n.get(model + "." + toCamelCase(field))}
  </label>
  ${unescape(body)}
  <div class="help-block">
    #for (error <- keyAndErrorMessages.getErrors(field))
      <div>${error}</div>
    #end
  </div>
</div>
```

```ssp:members/_form.html.ssp
<% withAttributes(Map("model" -> "member")) { %>

#do(layout("../tags/_formField.html.ssp", Map("field" -> "name_text")))
  <input type="text" id="name_text" name="name_text" class="form-control" value="${s.params.name_text}" />
#end

#do(layout("../tags/_formField.html.ssp", Map("field" -> "birthday_date")))
  <div class="row">
    <div class="col-xs-2">
      <input type="text" name="birthday_date_year"  class="form-control" value="${s.params.birthday_date_year}"  placeholder='${s.i18n.get("year")}'  maxlength=4 />
    </div>
    <div class="col-xs-2">
      <input type="text" name="birthday_date_month" class="form-control" value="${s.params.birthday_date_month}" placeholder='${s.i18n.get("month")}' maxlength=2 />
    </div>
    <div class="col-xs-2">
      <input type="text" name="birthday_date_day"   class="form-control" value="${s.params.birthday_date_day}"   placeholder='${s.i18n.get("day")}'   maxlength=2 />
    </div>
  </div>
#end

<% } %>
```