| ---- |
| Environmental Services |
| ---- |
| |
| Environmental Services |
| |
| Environmental 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 |
| {{{../ref/org/apache/tapestry5/corelib/components/Form.html}Form}} |
| component creates an environmental of type |
| {{{../../apidocs/org/apache/tapestry5/services/FormSupport.html}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 annotation |
| |
| The {{{../../apidocs/org/apache/tapestry5/annotations/Environmental.html}Environmental}} annotation |
| is used to dynamically connect to a Environmental service provided by an enclosing component. |
| |
| A very common Environmental is |
| {{{../../apidocs/org/apache/tapestry5/RenderSupport.html}RenderSupport}}, used |
| when generating {{{ajax.html}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 {{{../../apidocs/org/apache/tapestry5/services/Environment.html}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 environment |
| |
| The 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 Environmentals |
| |
| Not 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 |
| {{{../../apidocs/org/apache/tapestry5/services/MarkupRendererFilter.html}MarkupRendererFilter}} |
| contributions to the |
| {{{../../apidocs/org/apache/tapestry5/service/MarkupRenderer.html}MarkupRenderer}} service. |
| |
| Accessing Environmentals in Services |
| |
| The 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. |