Tapestry 5 は革新的な特徴を備えています。コンポーネントMixin です。コンポーネントMixinは巧妙なコンセプトです; Mixinと呼ばれる特別なコンポーネントを正統なコンポーネントに混ぜ合わせることができます。 コンポーネントとそのMixinはコンポーネントテンプレート内でひとつのタグとして表されますが、コンポーネントとMixinのすべての振る舞いを持っています。
入力フィールドに検証を追加したり、Ajaxの効果や動作を全てのコンポーネントに追加するためにMixinは設計されました。
Mixinはコンポーネントのマッシュアップと考えることができます; Mixinの振る舞いをコンポーネントの振る舞いに結合させ、一カ所に束ねるのです。
Mixinはふたつの異なるシナリオで用いられます: インスタンスMixin と 実装Mixin です。
Mixinクラスは、コンポーネントやページクラスのサブパッケージと同様に、 アプリケーション(またはライブラリの)ルートパッケージ下の mixins サブパッケージに配置します。
パッケージが異なること以外、Mixinクラスはコンポーネントクラスとまったく同じです。
今のところ、コンポーネントにできることなら、パラメータやレンダーフェーズメソッドも含めてすべてMixinにもすることができます。
Mixinはテンプレートを持つことができません。 レンダーフェーズメソッドの実行という点では、Mixinはコンポーネントに完全に統合されています。
Mixinは永続フィールドを持つことができますが、今のところこれは完璧には実装されていません (Mixinとコンポーネントの間で、あるいは他のMixinとの間で名前の衝突が起こる可能性があります)。 Mixinで永続フィールドを使用する場合は十分に注意してください ... もっと良いのは永続値の保存はパラメータ経由でコンテナに委譲することです。
Mixin自身がMixinを持つことはできません。
インスタンスMixinとは、コンポーネントのある特定の インスタンス に対してMixinを適用することです。 これはコンポーネントテンプレート内で<comp>要素のmixinsアトリビュートを用いて行います。 mixinsアトリビュートにはMixin名のカンマ区切りリストを与えます。
Componentアノテーション を用いてコンポーネントの型を定義する際にMixinを適用することもできます。その場合は次のふたつの方法があります:
ひとつ目の方法は記述量が少なくて済みます。また、コアMixinをアプリケーション独自のMixinで置き換えることができます。 ふたつ目の方法はより具体的で、またリファクタリングしやすいです (Mixinクラスの名前を変更すると、MixinClassesアノテーション内に書かれたクラス名も変更されるでしょう)。
@Component(parameters=. . .) @Mixins({"Autocomplete", "DefaultFromCookie"}) private TextField userId;
この例ではTextField型のコンポーネントと、AutocompleteおよびDefaultFromCookieという 仮想の Mixinを定義しています。
実装Mixinはあるコンポーネントのインスタンス全てに適用されるMixinです。 実装Mixinには Mixinアノテーション を使用します。 このアノテーションはMixinインスタンスを参照するフィールドを定義します。
public class AutocompleteField extendes TextField { @Mixin private Autocomplete autocompleteMixin; . . . }
多くの場合、インスタンス化するMixinの具象クラスをフィールドの型とします。
しかし、インターフェースや基底クラスをフィールドの型とする場合は、アノテーションのvalueアトリビュートを用いてMixinクラス名を指定することができます:
public class AutocompleteField extendes TextField { @Mixin("Autocomplete") private Object autocompleteMixin; . . . }
Mixinはコンポーネントと全く同じようにパラメータを持つことができます。
(テンプレート内であっても、Componentアノテーションのparametersアトリビュートを使う場合でも)パラメータをバインドする際に、 Tapestryは(コンポーネントとMixinの)各クラスで定義されているパラメータから各パラメータ名に対応するものを見つけます。
コンポーネントとMixinがどちらも同じ名前のパラメータを定義している場合、コンポーネントが勝ちます: コンポーネントのパラメータがバインドされMixinのパラメータはバインドされません。
しかし、パラメータ名の前にMixinクラスの 非限定 名を付けることで、曖昧さを排除することができます。例:
@Component(parameters={"Autocomplete.id=auto", . . . }) @Mixins("Autocomplete", "DefaultFromCookie"}) private TextField userId;
ひとつのコンポーネントに統合された全てのMixinは、ほとんどのフェーズでそのコンポーネントよりも 前に レンダーフェーズメソッドを実行します。 しかし、終盤のフェーズ(AfterRender, CleanupRender)では実行順序は逆になります。
例外: MixinAfter でアノテーションされたMixinはコンポーネントの前ではなく 後に 実行されます。