A Component Mixin is a way to supplement an existing Tapestry component with additional behavior.
Div |
---|
Wiki Markup |
---|
{ | float:right | title | Related Articles |
---|
class | aui-label |
---|
|
Content by Label |
---|
showLabels | false |
---|
showSpace | false |
---|
title | Related Articles |
---|
cql | label in ("component-classes","mixins") and space = currentSpace() |
---|
| |background=#eee}
{contentbylabel:title=Related Articles|showLabels=false|showSpace=false|space=@self|labels=component-classes,mixins}
{float} |
You can think of a mixin as a kind of mashup for a component; it combines the new behavior of the mixin with the existing behavior of the component, and bundles it all in one place. Mixins may be used to add specialized validation to user input fields, dynamically modify the HTML output of a component, or to add Ajax effects and behaviors to of all sorts of to components.
Tapestry comes with several mixins, such as the Autocomplete mixin which adds autocomplete behavior to an ordinary TextField Component. In addition, you can easily create your own.
...
An instance mixin is a mixin applied to a specific instance of a component. This can be done in the component template with the mixins
attribute of the component tag. This is a comma-separated list of mixin names.
Code Block |
---|
|
<t:textfield t:id="accountName" t:mixins="Autocomplete,DefaultFromCookie" />
|
...
The former is often less verbose, and allows core mixins to be overridden with application-specific mixins. The later format is more specific and more refactor-safe (renaming a mixin class will rename the entry in the MixinClasses annotation as well).
Example:
Code Block |
---|
|
@Component(parameters=. . .) @Mixins({"Autocomplete", "DefaultFromCookie"})
private TextField userId;
|
...
With @Mixins and @MixinClasses annotations, we can order the list of mixins, by adding a constraint.
Code Block |
---|
|
@Component(parameters=. . .) @Mixins({"Autocomplete", "DefaultFromCookie::before:Autocomplete"})
private TextField userId;
|
Code Block |
---|
|
@Component(parameters=. . .)
@MixinClasses(value={Autocomplete.class, DefaultFromCookie.class}, order={"","before:AutoComplete"})
private TextField userId;
|
...
Implementation mixins, mixins which apply to all instances of a component, are added using the @Mixin annotation. This annotation defines a field that will contain the mixin instance.
Code Block |
---|
|
public class AutocompleteField extends TextField
{
@Mixin
private Autocomplete autocompleteMixin;
. . .
}
|
...
In other cases, such as when the field's type is an interface or a base class, the value attribute of the annotation will be used to determine the mixin class name:
Code Block |
---|
|
public class AutocompleteField extends TextField
{
@Mixin("Autocomplete")
private Object autocompleteMixin;
. . .
}
|
...
Mixins are allowed to have parameters, just like components.
When binding parameters (either in the template, or using the parameters attribute of the Component annotation), Tapestry will match each parameter name against the parameters defined by each class (which is to say, the component and each mixin).
If the component and a mixin both define a parameter with the same name, then the component wins: the component's parameter will be bound, and the mixin's parameter will be unbound.
Alternately, you may prefix the name of the parameter with the unqualified name of the Mixin class; this eliminates the ambiguity. Example:
Here we provide a value of ".5" seconds for the Autocomplete mixin's "frequency" parameter:
Code Block |
---|
|
<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
...
<t:textfield t:id="accountName" t:mixins="Autocomplete" t:Autocomplete.frequency=".5" /> |
The parameter name should be prefixed with the name of the mixin class ("Autocomplete." above).
You can do the same thing with the @Component annotation:
Code Block |
---|
|
Code Block |
---|
java | java |
@Component(parameters={"Autocomplete.idfrequency=auto.5", . . . })
@Mixins("Autocomplete", "DefaultFromCookie"})
private TextField userId;
|
When using the Tapestry 5.3 and earlier XSDs, you may omit the class name prefix (e.g. t:frequency=".5" instead of t:Autocomplete.frequency=".5"), but then Tapestry has to resolve conflicts. If a component and a mixin both define a parameter with the same name, the component wins; the component's parameter will be bound, and the mixin's parameter will be unbound.
If the component and a mixin both supports informal parameters, the mixin will receive the all the unqualified informal parameters. If more than one Mixin supports informal parameters the results are undefined.
...
It is sometimes desirable to access the current value of a parameter defined in the component associated with the mixin. For example: normally, when the textfield component is marked disabled, it renders a text field with a disabled attribute, but you want it to output the (plaintext) value when disabled. A mixin for this purpose would need access to at least the disabled, and value parameters, and possibly the translate parameter (for a client-side representation). You can access the disabled parameter via @InjectContainer and checking isDisabled on the field, but textfield currently provides no access to value or translate. In this case, you can bind the core-component parameter using the @BindParameter annotation:
Code Block |
---|
|
public class MyMixin
{
@BindParameter
private boolean disabled;
@BindParameter
private FieldTranslator translate;
@BindParameter
private Object value;
Boolean beginRender(MarkupWriter writer)
{
...
if (disabled)
{
...
String stringValue = translate.toClient(value));
...
}
...
}
....
|
...
By default, Tapestry will bind the parameter with the same name as the field. You can explicitly declare the parameter to bind via the value attribute:
Code Block |
---|
|
@BindParameter("translate")
private FieldTranslator translator;
|
In same some cases, a mixin will be used on different components using different names for a similar parameter type. For instance, BeanEditor has an "object" parameter; most form fields have a "value" parameter, and Grid has a "source" parameter. These parameters have different names but share the feature of being , but each is the "principle" parameter on which the components are acting. A mixin useable by all three components can specify multiple potential parameter values to bind. The first value that matches a declared parameter of the associated component will be used:
Code Block |
---|
|
public class MyMixin
{
...
@BindParameter({"value","object","source"})
private Object principalObject;
...
}
|
...
Withing a given phase and class (@MixinAfter vs. mixin before), mixin ordering is determined by the ordering constraints specified in the mixin definitions. The constraint definitions follow the same conventions as ordered service configurations. How you specify the constraints depends on how the mixin is specified.
Code Block |
---|
language | java |
---|
title | As an Implementation Mixin |
---|
|
@Mixin("Autocomplete",order={"before:DiscardBody","after:RenderDisabled"}
private TextField userId;
|
Code Block |
---|
xmllanguage | xml | title | As a Template-specifed Instance Mixin |
---|
|
<input t:id="myfield" t:mixins="autocomplete::before:discardbody;after:renderdisabled,
defaultfromcookie::before:autocomplete"/>
|
Code Block |
---|
language | java |
---|
title | As a @Mixins-specified Instance Mixin |
---|
|
@Component(...)
@Mixins("Autocomplete::before:discardbody;after:renderdisabled","DefaultFromCookie::before:autocomplete"))
private TextField userId;
|
Code Block |
---|
language | java |
---|
title | As a @MixinClasses-specified Instance Mixins |
---|
|
@Component(...)
@MixinClasses(value={Autocomplete.class,DefaultFromCookie.class},
order={"before:discardbody;after:renderdisabled","before:autocomplete")
|
The ordering is always specified in terms of the order of the "forward" rendering process (setupRender, beginRender, etc.). When the "reverse" rendering phases (afterRender, etc.) occur, the mixin order is exactly reversed. Mixins which have no associated ordering constraints will be ordered in a manner which is consistent with the specified constraints for all other mixins, but is otherwise unspecified.
Available Mixins
...
...
In addition, the following mixins are available from other sources:
ClickOnce | From JumpStart, a mixin to apply to a submit button, ensuring it can't be double-clicked |
Confirm | Adds a JavaScript confirm prompt to any link |
ZoneUpdater | Updates a zone when a client-side event occurs |
Additional Tools
Tapestry-Xpath is a third-part Tapestry module that allows XPath traversal of the Tapestry (server-side) DOM, which can be extremely useful in certain mixins.