Polymer 1.0とBootstrap v3を組み合わせる


laco0416です。Polymer 1.0からBootstrapのテーマを自作Custom Elementsに適用するのが簡単になった話をします。

# 0.5から1.0で変わったこと
[前回の記事](http://qiita.com/laco0416/items/f5a36934c1c8d047d521)に書いたとおり、Polymer 0.5系におけるShadow DOMの実装はW3Cの仕様に忠実に、Shadow DOMの外からのCSSの侵入を防ぐようになっていました。しかしPolymer 1.x系では実装を軽量で高速な **Shady DOM** に切り替えたことで、CSSのスコープは内から外への漏洩を防ぐのみとなりました(W3Cの仕様からはズレますが…)。

この変更によりCustom Elements内でグローバルのスタイルを柔軟に適用できるようになったので、Bootstrapのようなテーマと共に使うのがごくごく自然な書き方で実現できます。


# PolymerとBootstrapを組み合わせる意義
WebComponentsのないHTMLはスタイリングのためだけにdivネスト地獄を容易に引き起こします。例えば名前の部分だけスタイルを変えるネームタグを作る次のようなHTMLはセマンティックでない構造をしています

```html
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div>
```

我々がやりたいことは「ネームタグを描画する」ことです。そしてそのために必要な情報だけをもつ構造は次の形です

```html
<div id="nameTag">Bob</div>
```

これでは`#nameTag`の要素にしか適用できないので、汎用的にDOM要素として配置できるようにするのがWebComponentsであり、Polymerです。

```html
<name-tag name="Bob"></name-tag>
```

誰がどうみてもこのタグはネームタグに「Bob」を表示する要素ですね。
WebComponentsはこのように、divの入れ子構造による非セマンティックなスタイリングを取り払い、DOM構造に意味を持たせることが可能な技術です。

[Bootstrap](http://getbootstrap.com/)は御存知の通り、だれでも(デザインなんてわからないプログラマーでも！)簡単にレスポンシブで、それなりの見た目のWebページ、アプリケーションが作れるCSSテーマとJSのツールキットのセットです。そして、Bootstrapはとりわけdivネストが深くなりがちです。BootstrapとPolymerを組み合わせることで、少しでもセマンティックなindex.htmlを書くことは、三ヶ月後にそのコードを読む自分を少なからず幸せにするでしょう。

# サンプル
Bootstrapにはいくつかのサンプルが用意されていますが、その中で今回は「[Jumbotron](http://getbootstrap.com/getting-started/#jumbotron)」というサンプルの中にある小さなパーツをPolymerで作ってみました。

![image](https://www.evernote.com/l/AlfQ79YjgclK3pM1d1J4ocZfENqKeYM-dggB/image.png)

完成品がこちらの`<row-item>`。ドキュメントから`header`と`link-to`を属性として指定し、コンテンツ部分は分散ノードとして取り込みました

```html
<!DOCTYPE html>
<html>
<head lang="ja">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Bootstrap with Polymer</title>
  <script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
  <link rel="import" href="elements/row-item.html"/>
</head>
<body>
  <div class="container">
    <div class="row">
      <row-item class="col-md-4" header="ヘッダーA" link-to="#A">
        <p>Content of A</p>
      </row-item>
      <row-item class="col-md-4" header="ヘッダーB" link-to="#B">
        <p>Content of B</p>
      </row-item>
      <row-item class="col-md-4" header="ヘッダーC" link-to="#C">
        <p>Content of C</p>
      </row-item>
    </div>
  </div>
</body>
</html>
```

![image](https://www.evernote.com/l/AleXjYLJ3Y1NwqhNQfe26-XWAX7T84xCf3sB/image.png)



# 準備
bowerで必要なパッケージをインストールします

```
> bower install --save polymer PolymerElements/paper-elements bootstrap
```

エントリポイントになるHTML(ドキュメント)でWebComponentsのPolyfillとBootstrapのCSSファイルを読み込みます。

```index.html
<!DOCTYPE html>
<html>
<head lang="ja">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Bootstrap with Polymer</title>
  <script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
</head>
<body>
</body>
</html>
```

# コンポーネント作成
element/row-item.htmlを作成します。今回はドキュメントからBootstrapのスタイルが降ってくるので、コンポーネント内からBootstrapを呼び出す必要はありません。もちろん広く配布したいコンポーネントであればコンポーネント自体をBootstrapに依存させて`<link>`でCSSファイルを読み込むことも可能です。([参考](http://qiita.com/laco0416/items/f5a36934c1c8d047d521#custom-elements%E3%81%8B%E3%82%89css%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%80))

```html
<link rel="import" href="../bower_components/polymer/polymer.html"/>
<link rel="import" href="../bower_components/paper-button/paper-button.html"/>

<dom-module id="row-item">
  <template>
    <h2>{{header}}</h2>

    <content></content>

    <paper-button class="btn btn-primary" on-click="goLink">View details &raquo;</paper-button>
  </template>
  <script>
    Polymer({
      is: 'row-item',
      properties: {
        header: {
          type: String,
          value: "Heading"
        },
        linkTo: {
          type: String
        }
      },
      goLink: function () {
        window.location.href = this.linkTo;
      }
    });
  </script>
</dom-module>
```

## プロパティの宣言
コンポーネントを使用する側からAttributeとして値を渡すには`properties`の中でプロパティを宣言する必要があります。

```js
    Polymer({
      is: 'row-item',
      properties: {
        header: {
          type: String,
          value: "Heading"
        },
        linkTo: String
      }
    });
```

プロパティは名前をキーにオブジェクトもしくは型を指定します。オブジェクトを指定する場合も`type`プロパティは必須です。`value`は初期値になります。

プロパティを文字列としてDOM中で展開するには`{{}}`を使います

```html
<h2>{{header}}</h2>
```

イベントハンドラ内で使う場合は`this`のプロパティとして呼び出せます

```js
      goLink: function () {
        window.location.href = this.linkTo;
      }
```

イベントリスナは通常のHTML要素と同じように記述できます。ハンドラーは`Polymer()`に渡すオブジェクトのプロパティとして宣言します。

```html
<paper-button class="btn btn-primary" on-click="goLink">View details &raquo;</paper-button>
```

```js
    Polymer({
      is: 'row-item',
      properties: {
        header: {
          type: String,
          value: "Heading"
        },
        linkTo: {
          type: String
        }
      },
      goLink: function () {
        window.location.href = this.linkTo;
      }
    });
```

## CSSテーマの適用
ここまでで気づいた方もいるかと思いますが、`paper-button`のクラスに`btn btn-primary`が指定されています。これはBootstrapで宣言されているボタン向けのスタイルです。

![image](https://www.evernote.com/l/AlegDNkPVTFEW59pZz3t00Dn1YepkDGaIMgB/image.png)

Bootstrap 3系はフラットデザインがベースになっているので、クリックした際のアニメーション等はありません。

この`<row-item>`ではPolymerのPaper Elementsに含まれる`<paper-button>`にこのクラスを設定しています。`<paper-button>`はマテリアルデザインのボタンスタイルになっていて、クリックした場所から波紋のようにアニメーションが広がります。

![image](https://www.evernote.com/l/Alcs8CeGsHtDxr0bsGxHWLz2dfog7_SZ6mUB/image.png)


今回`<paper-button>`に`btn btn-primary`を設定したことで、`<paper-button>`のアニメーションはそのままに、カラーリングやシェイプはBootstrapの`btn btn-primary`を適用することができました

![image](https://qiita-image-store.s3.amazonaws.com/0/20253/580bbd4b-fc0d-910c-3511-6b162139ce1d.gif "capture.gif")

# 配置
作成した`<row-item>`をドキュメントに配置します。

```html:index.html
<!DOCTYPE html>
<html>
<head lang="ja">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Bootstrap with Polymer</title>
  <script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
  <link rel="import" href="elements/row-item.html"/>
</head>
<body>
  <div class="container">
    <div class="row">
      <row-item class="col-md-4" header="ヘッダーA" link-to="#A">
        <p>Content of A</p>
      </row-item>
      <row-item class="col-md-4" header="ヘッダーB" link-to="#B">
        <p>Content of B</p>
      </row-item>
      <row-item class="col-md-4" header="ヘッダーC" link-to="#C">
        <p>Content of C</p>
      </row-item>
    </div>
  </div>
</body>
</html>
```

`<row-item>`も他のHTML要素と変わらず、`row`と`col-md-4`でレスポンシブに表示することができます。

# まとめ
Polymer 1.0のShady DOMのおかげで特にShadow DOMのCSSスコープがあることを感じずに簡単にコンポーネント中でBootstrapが使えました。BootstrapにかぎらずほとんどのCSSテーマが同様に使えると思います。既存のWebアプリケーションを部分的にでもPolymerでコンポーネント化してみてはどうでしょうか？