[Grails]Twitter Bootstrapを使う

参考：
http://grails.org/plugin/twitter-bootstrap
http://getbootstrap.com/
http://foodforcoding.com/2013/05/25/grails-with-twitter-bootstrap/

直接css等をダウンロードして利用する方法もあるけど、今回はプラグインを利用してみる。
どうしても手動でやりたい！という場合は[ココを参照](http://keaplogik.blogspot.de/2012/10/grails-part-4-enhanced-ui-design-w.html)


# プロジェクトの作成
とりあえず適当なディレクトリにテスト用のプロジェクトを作成。
今回は **bootstraptest** というプロジェクト名にした。

```
grails create-app bootstraptest
```

#プラグインのインストール

```groovy:grails-app/conf/BuildConfig.groovy
plugins {
    // 以下を追記
    runtime ':twitter-bootstrap:3.0.3'
}
```

Grailsのインタラクティブモードであれば、いったん抜ける。
そして`grails clean`、`grails compile`、`grails run-app`を順番に実行。
Grailsのインタラクティブモードやクラスパスの設定など、本来は動的に行われるはずだけど、よくリロードが上手く動かなかったりするので、プラグインのインストール時や、開発時に動作が怪しいと思った際には上記の **インタラクティブモードを抜ける -> clean -> compile -> run-app**を実行した方がスッキリ。

# 実際に試してみる
とりあえずテスト用のコントローラとビューを作成

```
grails> create-controller hello
| Created file grails-app/controllers/bootstraptest/HelloController.groovy
| Created file grails-app/views/hello
| Created file test/unit/bootstraptest/HelloControllerSpec.groovy
```

で、実際のビューファイルは作成してくれないので、手動で **grails-app/views/hello/index.gsp** を作成する。
そのindex.gspを以下のようにする。

```html:grails-app/views/hello/index.gsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
  <meta name="layout" content="main"/>
  <title></title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
```

そして、 **grails-app/views/layouts/main.gsp** を以下のようにする。

```html:grails-app/views/layouts/main.gsp
<!DOCTYPE html>
<html lang="en">
<head>
    <r:require modules="bootstrap"/>
    <g:layoutTitle/>
    <r:layoutResources/>
</head>
<body>
<g:layoutBody/>
<r:layoutResources/>
</body>
</html>
```

ちなみにこのmain.gspがいわゆるテンプレートになっていて、GrailsではこれをSitemeshを利用して実現しています。
[ドキュメントはここ](http://grails.jp/doc/latest/guide/theWebLayer.html#layouts)
基本的にこれで完了。
実際に[http://localhost:8080/bootstraptest/hello/index]()にアクセスするとBootstrapが適用されているのが確認できるはず。

#もう少し具体的に
[http://getbootstrap.com/examples/navbar/]()を参考にしてみる。
1. **web-app/css/navbar.css** を作成し、以下のようにする。

```css:web-app/css/navbar.css
body {
    padding-top: 20px;
    padding-bottom: 20px;
}

.navbar {
    margin-bottom: 20px;
}
```

2. **grails-app/views/layouts/main.gsp** を以下のように修正する。

```html:grails-app/views/layouts/main.gsp
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="${resource(dir: 'css', file: 'navbar.css')}" type="text/css">
    <r:require modules="bootstrap"/>

    <g:layoutTitle/>
    <r:layoutResources/>
</head>
<body>

<div class="container">

    <!-- Static navbar -->
    <div class="navbar navbar-default" role="navigation">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">Link</a></li>
                <li><a href="#">Link</a></li>
                <li><a href="#">Link</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li class="divider"></li>
                        <li class="dropdown-header">Nav header</li>
                        <li><a href="#">Separated link</a></li>
                        <li><a href="#">One more separated link</a></li>
                    </ul>
                </li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
                <li class="active"><a href="./">Default</a></li>
                <li><a href="../navbar-static-top/">Static top</a></li>
                <li><a href="../navbar-fixed-top/">Fixed top</a></li>
            </ul>
        </div><!--/.nav-collapse -->
    </div>

    <!-- Main component for a primary marketing message or call to action -->
    <div class="jumbotron">
    <g:layoutBody/>
    </div> <!-- /container -->
    <r:layoutResources/>
</body>
</html>
```

これで[http://localhost:8080/bootstraptest/hello/index]()に再度アクセスするとイケてる画面が表示される。
上手く反映されなかったりする場合は、 **インタラクティブモードを抜ける -> clean -> compile -> run-app** の黄金パターンを実行する。

# scaffoldも試してみる
適当にドメインを作成・・・今回はBookというドメインを生成

```
grails> create-domain-class Book
| Created file grails-app/domain/bootstraptest/Book.groovy
| Created file test/unit/bootstraptest/BookSpec.groovy
grails>
```

出来上がったBook.groovyに適当にプロパティを追加

```groovy:grails-app/domain/bootstraptest/Book.groovy
package bootstraptest

class Book {

    String name
    Boolean enable = false
    Integer price
    
    static constraints = {
    }
}
```

scaffold用のコントローラとビューを生成

```
grails> generate-all bootstraptest.Book
| Finished generation for domain class bootstraptest.Book
grails>
```

これで[http://localhost:8080/bootstraptest/book/index]()にアクセスすれば！

###...なんがダサい・・・

まぁこれは当然で、そもそもデフォルトのscaffoldはBootstrapなんて知らないよ！という。
なので、scaffoldが利用しているデフォルトのデザインを変更してあげます。

# scaffoldのデザインを変更
`install-templates`コマンドを使ってデフォルトのデザインを編集できるようにします。

```
grails> install-templates
| Templates installed successfully
```
上記のコマンドを実行すれば、Grailsが自動生成するデフォルトのデザインが編集できるようになります。
 **src/templates/** がそれで、今回はscaffodingのテンプレートをいじりたいから **src/templates/scafflding** が対象。

デフォルトで生成されたものは当然今までscaffoldで利用されていたものなので、もちろんこのままではダメ。
[example grails project](https://github.com/robfletcher/twitter-bootstrap-scaffolding)からプロジェクト全体をzipで落としてきて、 **src/templates/scafflding** の中の **create.gsp**、 **edit.gsp**、 **list.gsp**、 **show.gsp**の4つを上書きする。

##その際に注意点が３つ。
1.今回はmain.gspを編集してテンプレートと利用しているため。`<meta name="layout" content="bootstrap">`を`<meta name="layout" content="main">`に変更する必要がある。

2.`<f:all bean="${propertyName}"/>`を`<g:render template="form"/>`に変更する。(ダウンロードしてきた **create.gsp** と **edit.gsp**)。
これは、ダウンロードしたサンプルが[Fields Plugin](http://grails.org/plugins/fields)を利用しているため。
今回このプラグインは利用しないので、書き換える必要がある。
（メモ：fieldsプラグインを使えば細かくフォームデザインをカスタマイズできるのかも？[参考サイト](http://www.objectpartners.com/2013/01/16/customizing-grails-scaffolding-and-adding-accessibility-with-the-fields-plugin/)）

3.テンプレートの編集は、Grailsを再起動しないと反映されない。

つぎに、BookControllerを動的scaffoldにしておく

```groovy:grails-app/domain/bootstraptest/Book.groovy
package bootstraptest

import static org.springframework.http.HttpStatus.*
import grails.transaction.Transactional

@Transactional(readOnly = true)
class BookController {

    static scaffold = Book
}

```
これでやっと完成！[http://localhost:8080/bootstraptest/book/list]()にアクセスすれば確認できます。
もちろんこのままだと・・・という人も居ると思うので、そういう人は適宜自分でテンプレートを修正すればOK.

実際に運用していかないとどういう問題が出るか分からないけど、scaffold用のSitemeshとその他のページのSitemeshは分けておいた方が無難かも・・・？

なお、今回はテンプレートの **Controller.groovy** を編集していないので、静的scaffoldを利用する際には、お空うこちらも修正しないといけないかも？