これは Mayaa Advent Calendar 2015 の4日目です。昨日は「MayaaでWebサイトのビューを実装するときに押さえておくべき基本テクニック1 – タグの命名規則と実装方法」でした。
昨日は、命名規則についての注意点を説明しました。今日はそれを軸に、各m:idの実装の仕方の注意点を説明します。
いしがみメソッド2. m:id実装時の注意すること
1. LOOP系のm:idはindexには長い名前を使い、maxを十分に大きな値を設定する
m:forを使ってfor文ライクなループを作るとき、インデックスの変数はiやjではなく、長い名前をつけることをおすすめします。これは、内側に含めるm:idが使う変数にどのインデックスが利くのかわからなくなってしまうためです。
また、for系のプロセッサーはmax属性で指定した回数以上ループすると、エラーが発生してしまいます。maxのデフォルト値は256ですが、256回制限は簡単に超えてしまいます。256を超えたら出力が止まるのではなく、エラーになるので、max属性を常に10000くらいに指定するべきです。
<m:for m:id="LOOP_ITEM" init="${var loopItemIndex = 0;}" test="${loopItemIndex < items.length;}" after="${loopItemIndex++}" max="10000"
別途、ユーザーに指定した回数でループを打ち切る機能を作るのがベストです。それにはテンプレート記述者にパラメータを指定される方法が必要ですので、このやり方は今後紹介します。
2. writeプロセッサーでエスケープを解除するときは全部する
m:writeプロセッサーは、標準では常に改行・XML特殊文字・ホワイトスペース文字をエスケープします。XSSの心配がない箇所で動的にタグを出力したい場合など、エスケープを解除する場合は
<m:write m:id="INFO_HTML_HERE" escapeXml="false" />
のように書けますが、m:idごとにエスケープの設定が変わると混乱を招きます。
ので、3種類のescapeは全て同時にfalseにしましょう。
<m:write m:id="INFO_HTML_HERE" escapeXml="false" escapeWhitespace="false" escapeEol="false" />
3. 混乱を招く機能を無効にする
Mayaaの標準設定では、m:id属性ではなく、id属性でもプロセッサーと紐付けられるようになっていますが、デザイナーと分業をする際に混乱を招くため無効にしましょう。
また、XPath機能は使うべきではありません。
理由としては、デザイナーから見て、暗黙の動作のように見えてしまうことがあります。これは後々必ず混乱を招きます。
パフォーマンスにも問題があるので、これも無効にしてしまいましょう。
さらに、m:injectを使ってのプロセッサーの解決も、分業する上では不要な機能なので、無効にするべきです。
よって、次のようになります。
参考: 5-6. id属性を無視する
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE provider
PUBLIC "-//The Seasar Foundation//DTD Mayaa Provider 1.0//EN"
"http://mayaa.seasar.org/dtd/mayaa-provider_1_0.dtd">
<provider>
<templateBuilder
class="org.seasar.mayaa.impl.builder.TemplateBuilderImpl">
<resolver class="org.seasar.mayaa.impl.
builder.injection.MetaValuesSetter"/>
<resolver class="org.seasar.mayaa.impl.
builder.injection.ReplaceSetter"/>
<resolver class="org.seasar.mayaa.impl.
builder.injection.RenderedSetter"/>
<resolver class="org.seasar.mayaa.impl.
builder.injection.InsertSetter"/>
<!--
<resolver class="org.seasar.mayaa.impl.
builder.injection.InjectAttributeInjectionResolver"/>
-->
<resolver class="org.seasar.mayaa.impl.
builder.injection.EqualsIDInjectionResolver">
<parameter name="reportUnresolvedID" value="true"/>
<parameter name="reportDuplicatedID" value="true"/>
<!-- HTMLのidとXHTMLのidを追加しないようコメントアウト
<parameter name="addAttribute"
value="{http://www.w3.org/TR/html4}id"/>
<parameter name="addAttribute"
value="{http://www.w3.org/1999/xhtml}id"/>
-->
</resolver>
<!--
<resolver class="org.seasar.mayaa.impl.builder.
injection.XPathMatchesInjectionResolver"/>
-->
<parameter name="outputTemplateWhitespace" value="true"/>
<parameter name="outputMayaaWhitespace" value="false"/>
<parameter name="optimize" value="true"/>
</templateBuilder>
</provider>
余談ですが、将来ここに、独自のInjectionResolverを追加していくようになればMayaa上級者です!
4. 名前は英語にこだわらない
m:idの名前はHTMLタグの属性名として指定します。プログラマーはついメソッドを定義するときのように、英語的な名前付けをしてしまいますが、HTMLファイルが横に長くなってしまって読みづらくなってしまうので、積極的に省略しましょう。
IF_IS_MEMBER
ではなく
IF_MEMBER
とします。
5. tableタグだけは特別に扱う
table
タグはMayaaと相性が悪いタグの一つです。
div
タグのように、無限に包むことが出来ず、
table > tbody > tr > td
のように階層構造が決まっているため、例えば、trタグにm:idを既に適用してしまうと、そのtrをある条件にもとづいて出し分けなどが出来ません。
ダメな例:
<table>
<tbody>
<div m:id="IF_MEMBER">
<tr m:id="ROW1_TAG">
<td>aaa</td>
<td>bbb</td>
<td>ccc</td>
</tr>
</div>
<tr m:id="ROW2_TAG">
<td>aaa</td>
<td>bbb</td>
<td>ccc</td>
</tr>
</tbody>
</table>
この書き方は出来ません。divタグをtbodyとtrの間に書くことが出来ないためです。IF_ROW3をm:echoなしのifプロセッサーにして、タグが消えるようにすれば、サーバー側では大丈夫になりますが、テンプレート側で見た時に表示が崩れてしまいます。
そこで、せっかくIF_MEMBERのように流用できるm:idがあったとしても、ROW1_TAGの方に、ifプロセッサーを追加して次のようにしてください。
<m:if m:id="ROW1_TAG" test="${context.isMember()}">
<m:echo>
<m:attribute name="xxx" value="yyy" />
<m:doBody />
<m:echo>
</m:if>
まとめ
地味なルールですが、これらを守ることであとあと混乱を防ぐということが効いてきます。
Mayaaはとても汎用的に作られていて、初期状態では、大変ゆるい状態でリリースされている印象を持ちます。
しかし、チームで作業をする場合は例えば、
InjectAttributeInjectionResolver
のようなものは使わず、必要最小限の機能で賄う戦略を取ることが結局効率が良いです。
さらに、次回はもっと面白いテクニックを紹介しようと思います。