Environmental ServicesPage edited by Christophe Cordenier
Comment:
Fix broken links
Changes (5)
Full ContentEnvironmental ServicesEnvironmental services represent yet another, distinct form of injection. Unlike service injection (injection via a service implementation's constructor) or normal component injection (directly into component fields, via the @Inject annotation) where the injected value is always the same, with environmental services, the injected value is very late bound and dynamic. Environmental services are often a conduit of communication between an outer component and the components it encloses. An example of this is form support; the Form component creates an environmental of type FormSupport. The FormSupport interface allows enclosed components to participate in both the rendering of the Form and the Form's eventual submission. This is how control names and client-side ids are determined, how fields register callbacks so that they can process their part of the submission, and how fields hook themselves to client-side validation. Using the @Environmental annotationThe Environmental annotation is used to dynamically connect to a Environmental service provided by an enclosing component. A very common Environmental is RenderSupport, used when generating client-side _javascript_. @Inject @Path("${tapestry.scriptaculous}/dragdrop.js") private Asset dragDropLibrary;
@Environmental
private RenderSupport renderSupport;
void setupRender()
{
renderSupport.addScriptLink(dragDropLibrary);
}
Environmental services are, by their nature, per-thread (and therefore per-request). Accessing an environmental field causes a lookup, by type, against the Environment service. Normally, an environmental of the specified type must be available in the Environment, or an exception is thrown when accessing the field. If the value of the Environmental annotation is false, then the environmental value is optional. Placing a value in the environmentThe Environment service has push() and pop() methods to put a value in the Environment, and discard it. For example, say you were building a tab-based menu system and you needed to allow an outer TabGroup component to communicate with inner Tab components, to control various aspects of presentation. The relevant information could be exposed as an interface, TabModel. public class TabGroup { @Inject private Environment environment; void beginRender() { environment.push(TabModel.class, new TabModelImpl(...)); } void afterRender() { environment.pop(TabModel.class); } } public class Tab { @Environmental private TabModel model; void beginRender(MarkupWriter writer) { ... } } Notice that when pushing a value into the Environment, you identify its type as well as the instance. Environment maintains a number of stacks, one for each type. Thus, pushing a TabModel into the environment won't disturb the RenderSupport or other environmentals already there. What's important here is that the code that pushes a environmental onto a stack should also pop it off. The enclosed class, Tab, has full access to whatever object was pushed onto the stack by the TabGroup. The reason why Environment is a stack is so that a component can, when it makes sense, easily replace or intercept access to an Environmental. Fundamental EnvironmentalsNot all environmentals are pushed into the Environment by components. A number of environmentals are initialized as part of page rendering, even before the first component starts to render. This initialization is accomplished with MarkupRendererFilter contributions to the MarkupRenderer service. Accessing Environmentals in ServicesThe Environmenal annotation only works inside components. To access an Environmental inside a service implementation, you must inject the Environment service and obtain values from it using the peek() method. If this is something that will occur frequently, it is possible to create a service implementation that is "backed" by the Environment. For example, RenderSupport is accessible as a normal injection, because a service is built for it in TapestryModule: public RenderSupport buildRenderSupport(EnvironmentalShadowBuilder builder) { return builder.build(RenderSupport.class); } The EnvironmentShadowBuilder service creates a service implementation that delegates to the proper instance in the environment. The same technique can be used for your own services and environmentals.
Change Notification Preferences
View Online
|
View Changes
|
- [CONF] Apache Tapestry > Environmental Services confluence