blob: 1ddffc638254834b35b1b2c0faa10d0ecc33b4d8 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2005 The Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<document>
<properties>
<title>Property Injection</title>
</properties>
<body>
<section name="Property Injection">
<p>
Tapestry 4.0 introduces an entirely new concept into Tapestry application
development: property injection. By use of the
<a href="spec.html#spec.inject">&lt;inject&gt;</a>
element in page and component specifications, it is possible to add new properties
to pages or components, using the
<a href="spec.html#spec.inject">&lt;inject&gt;</a>
element in the page or component specification.
</p>
<p>
The new properties that are created often are more than just wrappers around an
instance variable; in many cases they create complex synthetic accessor methods.
</p>
<p>
There are different
<em>types</em>
of injected properties, defined by the type attribute of the
<a href="spec.html#spec.inject">&lt;inject&gt;</a>
element. The type determines how the object attribute is interpreted, and otherwise
guides how code for the property is generated at runtime. The default type is
<strong>object</strong>
.
</p>
<p>
Like so much in Tapestry, this list is open to extension. The
<a href="../tapestry-framework/hivedoc/config/tapestry.enhance.InjectWorkers.html">
tapestry.enhance.InjectWorkers
</a>
configuration point defines the available types of injection. The
<a href="../tapestry-framework/hivedoc/config/tapestry.enhance.EnhancementWorkers.html">
tapestry.enhance.EnhancementWorkers
</a>
configuration point defines an entire pipeline used to perform runtime code
enhancement (of which property injection is a critical phase).
</p>
<p>
In addition, many other elements support a property attribute; this is the name of a
property to create that holds the corresponding object. For example, the
<a href="spec.html#spec.bean">&lt;bean&gt;</a>
element's property allows access to a managed bean; the bean is
<em>still</em>
created on first reference. Components and assets may also be injected in this way.
</p>
<subsection name="meta injection">
<p>
The meta injection type provides a page or component with access to its meta
data. Meta data for a component is primarily provided as
<a href="spec.html#spec.meta">&lt;meta&gt;</a>
tags in the component or page specification.
</p>
<p>
However, meta-data may be specified elsewhere; the search starts in the
component (or page) specification, but if not defined there, the search
continues inside the component's namespace (its application or library
specification). If no value is found there, the search continues in the list of
application property sources. In other words, there are multiple places to set
defaults, which can be overridden.
</p>
<p>
Beyond wide searching, the other added value for the meta property injection is
<em>type coercion</em>
. Meta data always starts as simple strings, but your properties may be of any
type. Tapestry will attempt to coerce the string value to your desired type.
</p>
<p>
For example, perhaps you want to use meta-data to control the number of items
from some large list that is displayed on a single page. You can define the
meta-data, and the property in your page or component specification:
</p>
<source xml:space="preserve">
&lt;meta key="page.size" value="15"/&gt;
&lt;inject property="pageSize" type="meta" object="page.size"/&gt;
</source>
<p>You can access the this meta data value in code by defining a property:</p>
<source xml:space="preserve">
public abstract int getPageSize();
</source>
</subsection><!-- injection.meta -->
<subsection name="object injection">
<p>
The most common kind of injection, because "object" is the default injection
type. The object is a HiveMind object. The
<a href="hivemind.html">HiveMind integration documentation</a>
covers this type of injection in more detail.
</p>
<p>
</p>
</subsection><!-- injection.object -->
<subsection name="page injection">
<p>
Page injection allows a page to be injected into another page (or component).
Beneather the covers, the logic simply accesses the
<a
href="../apidocs/org/apache/tapestry/IRequestCycle.html">
IRequestCycle
</a>
object and obtains the page from it, and adds a cast if necessary.
</p>
<p>
The property type can be Object,
<a href="../apidocs/org/apache/tapestry/IPage.html">IPage</a>
, or any type assignable to
<a href="../apidocs/org/apache/tapestry/IPage.html">IPage</a>
.
</p>
<p>
This is often used with
<a href="listenermethods.html">listener method</a>
s. For example:
</p>
<source xml:space="preserve">
&lt;inject property="detailsPage" type="page" object="Details"/&gt;
</source>
<source xml:space="preserve">
public abstract Details getDetailsPage();
public IPage doShowDetails(long productId)
{
Details details = getDetailsPage();
details.setProductId(productId);
return details;
}
</source>
<p>
This is a common idiom: getting a page and casting it to its real type, invoking
methods upon it, then returning it (from the listener method), so that it is
activated to render the response.
</p>
</subsection><!-- injection.page -->
<subsection name="script injection">
<p>
Tapestry includes extensive support for creating client-side JavaScript. At the
core of this are specialized script templates. These templates must be parsed
into
<a href="../apidocs/org/apache/tapestry/IScript.html">
IScript
</a>
objects in order to be used. The script injection type hides the details of this
process, and simply represents the parsed script template as a read-only
property.
</p>
<p>
The object is the relative path to the script template; it is evaluated relative
to the page or component specification (typically, it is another file in the
same folder).
</p>
<p>This example is from the Palette component:</p>
<source xml:space="preserve">
&lt;inject property="script" type="script" object="Palette.script"/&gt;
</source>
<p>The script can then be executed from the Java code:</p>
<source xml:space="preserve">
public abstract IScript getScript();
. . .
PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
getScript().execute(cycle, pageRenderSupport, _symbols);
</source>
</subsection><!-- injection.script -->
<subsection name="state injection">
<p>
This style of injection allows easy access to
<a href="state.html#state.aso">Application State Objects</a>
, objects which store various kinds of global information (information needed on
many pages).
</p>
</subsection>
<subsection name="state-flag injection">
<p>
Using the
<a href="#injection.state">state</a>
injection can force the creation of an application state object; this type of
injection will create a
<em>read only</em>
boolean property that indicates
<em>if</em>
an application state object already exists.
</p>
<p>
This is used to
<em>prevent</em>
the creation of an application state object until it is absolutely needed.
</p>
<p>
For example; in a situtation where the user may or may not be logged in, you
would inject a state flag and the state in two properties and check the state
flag
<em>first</em>
:
</p>
<source xml:space="preserve">
// &lt;inject&gt; type state-flag in the XML
public abstract boolean getUserIdentityExists();
// &lt;inject&gt; type state in the XML
public abstract WizardState getUserIdentity();
public void doOperation()
{
if (getUserIdentityExists() &amp;&amp; getUserIdentity().isLoggedIn())
{
. . .
}
else
{
. . .
}
}
</source>
</subsection><!-- injection.state-flag -->
</section>
</body>
</document>