6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Assemble] 1ファイルにまとめているパーツを、読み込み時に受け渡す値に応じて分岐させたい

Last updated at Posted at 2015-01-08

基本

Assembleを使うとき、1ファイルに1パーツを書いているだけならそれを読み込ませるのは簡単。
ドキュメントを調べて載っているのはこのパターンが多い。
なので、このやり方はすぐ理解できる。

parts.hbs(パーツファイル)
<h1 class="title">Qiita</h1>
page.hbs(ビルドされるページファイル)
<html>
 <body>
 {{> parts}}
 <body>
</html>
page.html(ビルド結果)
<html>
 <body>
 <h1 class="title">Qiita</h1>
 <body>
</html>

応用

問題は、こういうことをしたいときに起こる。

page.html
<html>
 <body>
  <h2 class="subtitle red">HTML</h2>
 <h2 class="subtitle blue">CSS</h2>
 <body>
</html>

h2タグに注目してほしい。
中身のテキストと一部のクラスが違うだけで、ほぼ同じパーツ構成になっている。
これを1つのパーツファイルからうまい具合にクラスと文字列を分岐させて出力させたいと思うと、基本のやり方だけでは実現できない。

パーツの分析

結構シンブルなパーツだが、人間がこのパーツの出し分けについて考えることは割と複雑である。

<h2 class="subtitle red">HTML</h2>
<h2 class="subtitle blue">CSS</h2>
  • <h2 class="subtitle"></h2> この部分は全く共通。
  • HTML、CSSの部分は文字列で、いろんなタイトルが入ってくるので可変
  • red、blueのクラス名は各分岐先で決まっているので分岐ごとに固定。

パーツを作り、読み込ませる上でこれを実現しなければならない。
でなければ利用性とメンテナンス性が損なわれる。

extendを利用する

パーツファイルを個別に用意して、違う値の部分だけページファイルに記述して結果を出し分けることが出来る。

が、あくまでextendで読み込むパーツファイルの中身は基本的に全部出力されるので、
1ファイルに2パターン以上のパーツを併記できない。

パーツを1ファイルに併記しようとして{{#is}}ヘルパーを利用した場合、両方のパーツを1ページ内で使用することが不可能になる。

利用できるパターン

一応、望んだビルド結果にはなるパターン。

パーツをファイルごとに分ける

パーツは1ファイル1つしか書かない、ということであれば、これでも十分ではある。
ただ、今回の場合のような差が少ないパーツでも別ファイル化しなければならない。

両方のパーツで利用している共通クラス名が変われば、両方のファイルを変更する必要が出てくる。
2ファイルぐらいならどうとでもなるが、10、20と増えていくと、毎回の変更に応じて全ファイル更新するのは手間である。

subtitleRed.hbs(パーツファイル)
<h2 class="subtitle red">{{block "title"}}</h2>
subtitleBlue.hbs(パーツファイル)
<h2 class="subtitle blue">{{block "title"}}</h2>
page.hbs(ページファイル)
<html>
 <body>
 {{#extend subtitleRed}}
  {{#content "title"}}HTML{{/content}}
 {{/extend}}

 {{#extend subtitleBlue}}
  {{#content "title"}}CSS{{/content}}
 {{/extend}}
 </body>
</html>

パーツは共通。違う部分だけ渡す。

パーツはスッキリするが、ページファイルの記述が冗長になる。
できれば、クラス名は呼び出すパーツごとに固定なので、毎回生のクラス名を渡したくはない。

ただし、この使い方はこの使い方で利用価値があるので、覚えておいて損はない。

subtitle.hbs(パーツファイル)
<h2 class="subtitle {{block "class"}}>{{block "title"}}</h2>
page.hbs(ページファイル)
<html>
 <body>
 {{#extend subtitle}}
  {{#content "class"}}red{{/content}}
  {{#content "title"}}HTML{{/content}}
 {{/extend}}

 {{#extend subtitle}}
  {{#content "class"}}blue{{/content}}
  {{#content "title"}}CSS{{/content}}
 {{/extend}}
 </body>
</html>

NGパターン

{{#extend}}だけで、1つのパーツファイルをどうにか分岐させようとすると無理が生じる。
YFMと{{#is}}ヘルパーを併用して取り出せるのは、1通りだけ。
そのページで使用できるパーツはその1種類だけになってしまうので、必要に応じて両方のパーツを併用することができなくなる。

複数のパーツの共存ができなくなる、というのが最大の弊害。
color : redcolor : blueにすると、今度はredのパターンが出力不能に。

subtitle.hbs(パーツファイル)
{{#is color "red"}}
<h2 class="subtitle red">{{block "title"}}</h2>
{{/is}}

{{#is color "blue"}}
<h2 class="subtitle blue">{{block "title"}}</h2>
{{/is}}
page.hbs(ページファイル)
---
color : red
---
<html>
 <body>
{{#extend subtitle}}
  {{#content "title"}}HTML{{/content}}
{{/extend}}

{{#extend subtitle}}
  {{#content "title"}}CSS{{/content}}
{{/extend}}
 </body>
</html>
page.html(ビルド結果)
<html>
 <body>
  <h2 class="subtitle red">HTML</h2>
 <h2 class="subtitle red">CSS</h2> //←クラスがblueになってない
 </body>
</html>

parseJSONを利用する

基本的な使用法

これが一番応用が利いた。
{{#parseJSON}}で囲われている中でしか使われない、ローカル変数的な配列を提供する。
配列は中のパーツが呼び出された時の{{#is}}ヘルパーでの判定にも使えるし(もちろんその他のヘルパーの判定にも)、中のコンテンツ部分に利用する文字列情報としても利用できる(ので、そのまま使うだけでなくヘルパーを利用した何かしらの加工も可能)。

スコープが限定されているので、領域外には配列が適用されず、
柔軟に値を指定でき、1ファイルに書いてある異なるパーツとの共存も容易だった。

やりたかったことは結局、「呼び出すパーツごとに特定の値を渡して、それで何らかの判定をしたり、渡した値を文字列として出したい」というものだったので、ぴったりだった。

パーツファイルは一つだけで分岐可能。
渡す情報としては、分岐条件と中のパーツで使いたい具体的な文字列情報を渡すだけでいい。

分岐条件を{{#is partsName "subtitleRed"}}のようにそれぞれのパーツの名前の形式にしてやると、1ファイルに定義された個別パーツの呼び出し的な感じになる。

subtitle.hbs(パーツファイル)
{{#is color "red"}}
<h2 class="subtitle red">{{title}}</h2>
{{/is}}

{{#is color "blue"}}
<h2 class="subtitle blue">{{title}}</h2>
{{/is}}
page.hbs(ページファイル)
<html>
 <body>
{{#parseJSON '{color: red, title: HTML}'}}
  {{> subtitle}}
{{/parseJSON}}

{{#parseJSON '{color: blue, title: CSS}'}}
  {{> subtitle}}
{{/parseJSON}}
 </body>
</html>
page.html(出力結果)
<html>
 <body>
  <h2 class="subtitle red">HTML</h2>
 <h2 class="subtitle blue">CSS</h2>
 <body>
</html>

パーツの記述を更にまとめて細かい部分だけ分岐させる

{{#contains}}ヘルパーを利用し、1つのキーに「パーツ名・分岐情報」を含ませて条件分岐させてみる。
このパーツしかないなら{{#contains partsName "subtitle"}}の条件は不要だが、他にもパーツの記述があるという想定。

subtitle.hbs(パーツファイル)
{{#contains partsName "subtitle"}}
<h2 class="subtitle
 {{#contains partsName "red"}} red{{/contains}}
 {{#contains partsName "blue"}} blue{{/contains}}
">{{title}}</h2>
{{/contains}}
page.hbs(ページファイル)
<html>
 <body>
{{#parseJSON '{partsName: subtitle_red, title: HTML}'}}
  {{> subtitle}}
{{/parseJSON}}

{{#parseJSON '{partsName: subtitle_blue, title: CSS}'}}
  {{> subtitle}}
{{/parseJSON}}
 </body>
</html>

不要な改行・余白を消す

上の記述でビルドすると、こうなる。

<h2 class="subtitle
 red
">HTML</h2>

<h2 class="subtitle
 blue
">CSS</h2>

grunt等を使って余白整形してもいいが、ここの「Whitespace Control」に無駄な余白を消す方法が載っている

具体的には、ヘルパーや変数呼び出しの{{の直後か}}の直前に「~」という記述を追加する
{{~#contains}}とすれば、このヘルパーより左側の改行・余白がなくなる。
{{#contains~}}とすれば、このヘルパーより右側の改行・余白がなくなる。
両側に適用したい場合は{{~#contains~}}となる。

それを踏まえるとこういう感じの記述になる。

subtitle.hbs(パーツファイル)
{{~#contains partsName "subtitle"~}}
<h2 class="subtitle
 {{~#contains partsName "red"}} red{{/contains~}}
 {{~#contains partsName "blue"}} blue{{/contains~}}
">{{title}}</h2>
{{~/contains~}}

備考

{{#parseJSON}}{{> subtitle}}の形式に落ち着いたが、
{{#parseJSON}}{{#extend}}を囲っても同じようなことが出来ると思う(が、中身の分だけコードが長くなる)。

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?