Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: fixed language param of code macro
Wiki Markup
{float:right|background=#eee}
{contentbylabel:title=Related Articles|showLabels=false|showSpace=false|space=@self|labels=expressions,component-classes,component-templates,parameters}
{float}

Component Parameters

Table of Contents

Component parameters are the primary means for a component instance and its container to communicate with each other. Parameters are used to configure component instances.

In the following example, page is a parameter of the pagelink component. The page parameter tells the pagelink component which page to go to when the user clicks on the rendered hyperlink:

Code Block
xml
languagexml

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
    <t:pagelink page="Index">Go Home</t:pagelink>
</html>

...

The component listed below is a looping component; it renders its body a number of times, defined by its start and end parameters (which set the boundaries of the loop). The component can update a result parameter bound to a property of its container; it will automatically count up or down depending on whether start or end is larger.

Code Block
java
languagejava

package org.example.app.components;

import org.apache.tapestry5.annotations.AfterRender;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.SetupRender;

public class Count
{
    @Parameter (value="1")
    private int start;

    @Parameter(required = true)
    private int end;

    @Parameter
    private int result;

    private boolean increment;

    @SetupRender
    void initializeValues()
    {
        result = start;
        increment = start < end;
    }

    @AfterRender
    boolean next()
    {
        if (increment)
        {
            int newResult = value + 1;

            if (newResult <= end)
            {
                result = newResult;
                return false;
            }
        }
        else
        {
            int newResult= value - 1;
            if (newResult>= end)
            {
                result = newResult;
                return false;
            }
        }
        return true;
    }
}

...

The component above can be referenced in another component or page template, and its parameters bound:

Code Block
xml
languagexml

<html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
    <p> Merry Christmas: <t:count end="3"> Ho! </t:count>
    </p>
</html>

...

For example, the following template code:

Code Block
xml
languagexml

<ul>
    <li t:type="loop" source="1..10" value="index">${index}</li>
</ul>

and the following Java code:

Code Block
java
languagejava

@Property
private int index;

... could be rewritten as just:

Code Block
xml
languagexml

<ul>
    <li t:type="loop" source="1..10" value="var:index">${var:index}</li>
</ul>

...

Assets bindings are used to specify assets Assets, static content served by Tapestry. By default, assets are located relative to the component class in your packaged application or module. This can be overridden by prefixing the path with "context:", in which case, the path is a context path from the root of the web application context. Because accessing context assets is relatively common, a separate "context:" binding prefix for that purpose exists (described below).

...

Context bindings are like asset bindings, but the path is always relative to the root of the web application context. This is intended for use inside templates, i.e.:

Code Block
xml
languagexml

  <img src="${context:images/icon.png}"/>

...

Parameters that are required must be bound. A runtime exception occurs if a component has unbound required parameters.

Code Block
languagejava
public class Component{

  @Parameter(required = true)
  private String parameter;

}

...

The @Parameter annotation's value element can be used to specify a binding expression that will be the default binding for the parameter if otherwise left unbound. Typically, this is the name of a property that that will compute the value on the fly.

Code Block
java
languagejava

@Parameter(value="defaultMessage") // or, equivalently, @Parameter("defaultMessage")
private String message;

@Parameter(required=true)
private int maxLength;

public String getDefaultMessage(){ 
	return String.format("Maximum field length is %d.", maxLength);
}

...

You generally should not use the Template Expansion syntax, ${...}, within component parameter bindings. Doing so results in the property inside the braces being converted to an (immutable) string, and will therefore result in a runtime exception if your component needs to update the value (whenever the default or explicit binding prefix is prop: or var:, since such component parameters are two-way bindings).

Section
Column
Code Block
languagexml
titleThis is rightxml

<t:textfield t:id="color" value="color"/>
Column
Code Block
languagexml
titleThis is wrongxml

<t:textfield t:id="color" value="${color}"/>

The general rule is, only use the ${...} syntax in non-Tapestry-controlled locations in your template, such as in attributes of ordinary HTML elements and in plain-text areas of your template.

Section
Column
Code Block
languagexml
titleThis is rightxml

<img src="${context:images/banner.png}"/>
Column
Code Block
languagexml
titleThis is wrongxml

<img src="context:images/banner.png"/>

...

Parameters are not simply variables; each parameter represents a connection, or binding, between a component and a property of its container. When using the prop: binding prefix, the component can force changes into a property of its container, just by assigning a value to its own instance variable.

Code Block
xml
languagexml

<t:layout xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
    <p> Countdown:
        <t:count start="5" end="1" result="index">
          ${index} ...
        </t:count>
    </p>
</t:layout>

Because the Count component updates its result parameter (the result field), the index property of the containing component is updated. Inside the Count's body, we output the current value of the index property, using the expansion ${index}. The resulting output will look something like:

Code Block
xml
languagexml

  <p> Countdown: 5 ... 4 ... 3 ... 2 ... 1 ... </p>

...

Inherited bindings are useful for complex components; they are often used when an inner component has a default value for a parameter, and the outer component wants to make it possible to override that default.

Code Block
languagexml
titleIndex.tmlxml

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
  <body>
    <div t:type="layout" t:menuTitle="literal:The Title">
      ...
    </div>
  </body>
</html>
Code Block
languagexml
titleLayout.tmlxml

<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">

	<div t:type="title" t:title="inherit:menuTitle"></div>

	<t:body />

</t:container>
Code Block
languagejava
titleTitle.javajava

package org.example.app.components;

import org.apache.tapestry5.annotations.Parameter;

public class Title {

	@Parameter
	private String title;

}

...

Using this approach, the previous example may be rewritten as:

Code Block
java
languagejava

  @Parameter
  private String message;

  @Parameter(required=true)
  private int maxLength;

  @Inject
  private ComponentResources resources;

  @Inject
  private BindingSource bindingSource;

  Binding defaultMessage()
  {
    return bindingSource.newBinding("default value", resources, "basicMessage");
  }

  public String getBasicMessage()
  {
    return String.format("Maximum field length is %d.", maxLength);
  }

...

Alternately, the previous example may be written even more succinctly as:

Code Block
java
languagejava

  @Parameter
  private String message;

  @Parameter(required=true)
  private int maxLength;

  @Inject
  private ComponentResources resources;

  String defaultMessage()
  {
    return String.format("Maximum field length is %d.", maxLength);
  }

...

In rare cases, you may want to take different behaviors based on whether a parameter is bound or not. This can be accomplished by querying the component's resources, which can be injected into the component using the @Inject annotation:

Code Block
java
languagejava

public class MyComponent
{
  @Parameter
  private int myParam;

  @Inject
  private ComponentResources resources;

  @BeginRender
  void setup()
  {
      if (resources.isBound("myParam"))
      {
        . . .
      }
  }
}

...

In Tapestry 5.1 and later, you may use the publishParameters attribute of the @Component annotation. List one or more parameters separated by commas: those parameters of the inner/embedded component become parameters of the outer component. You should not define a parameter field in the outer component.

Code Block
languagexml
titleContainerComponent.tmlxml

<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
<t:pageLink t:id="link">Page Link</t:pageLink>
</t:container>
Code Block
languagejava
titleContainerComponent.java
public class ContainerComponent{
    @Component(id="link", 	publishParameters="page")
    private PageLink link;
}
Code Block
languagexml
titleIndex.tmlxml

<t:ContainerComponent t:id="Container" t:page="About" />

...