You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 18 Next »

Tapestry includes sophisticated JavaScript support, based on the Prototype and Scriptaculous libraries. These libraries are all packaged with Tapestry itself ... no extra download is required.

Tapestry will automatically link in prototype.js, scriptaculous.js, effects.js and the Tapestry library, tapestry.js. You can add additional libraries as needed.

Prototype and Scriptaculous Versions

Tapestry 5.2

Prototype 1.6.1

Scriptaculous 1.8.2

Tapestry 5.1

Prototype 1.6.0.3

Scriptaculous 1.8.2

Tapestry 5.0

Prototype 1.6.0

Scriptaculous 1.8.0

Changes to Scriptaculous

Tapestry uses a modified version of the main Scriptaculous library, scriptaculous.js, with the library's default autoloading behavior turned off. This lets Tapestry and Tapestry components control which Scriptaculus scripts are loaded, rather than having all of them loaded unnecessarily.

Basic JavaScript

The general strategy in Tapestry is that any significant amount of JavaScript should be packaged up as a static JavaScript library, a .js file that can be downloaded to the client.

Page specific JavaScript should be in the form of minimal statements to initialize objects, referencing the JavaScript libraries.

Most of this is accomplished via the RenderSupport object.

RenderSupport includes a number of methods that will be used by components, or event by services that are called from components.

addScriptLink()

void addScriptLink(Asset... scriptAssets);

This method adds a link to a script file, a JavaScript library. A component can inject such a script and pass one or more of assets to this method. Tapestry will ensure that the necessary <link> elements are added to the top of the document (just inside the <head> element).

Adding the same asset multiple times does not create duplicate links. The subsequent ones are simply ignored. In this way, each component can add the assets it needs, without worrying about conflicts with other components.

Note that the Prototype, Scriptaculous main and effects libraries, and the standard Tapestry library (which largely consists of support for form input validation) are included automatically.

If you need access to other Scriptaculous libraries, you can provide them as follows:


  @Inject @Path("${tapestry.scriptaculous}/dragdrop.js")
  private Asset dragDropLibrary;

  @Environmental
  private RenderSupport renderSupport;

  void setupRender()
  {
    renderSupport.addScriptLink(dragDropLibrary);
  }

The Asset is injected, using the ${tapestry.scriptaculous} symbol to reference the location of the Scriptaculous library.

The RenderSupport is accessed as an Environmental service.

The setupRender() method (the name is specifically linked to a render phase) is the correct place to inform the RenderSupport service that the library is needed.

Even though the dragdrop.js library is stored inside a JAR file, Tapestry ensures that it can be accessed from the client web browser. A Tapestry URL within the virtual folder "/assets" is created; the file will be given a version number (the application version number if not specified more specifically) and will be sent to the browser with a far-future expires header (to encourage the browser to cache the file aggresively).

addScript()

void addScript(String format, Object... arguments);

This method adds some initialization JavaScript to the page. By initialization we mean that it goes at the bottom of the document, and will only be executed when the document has finished loading on the client (i.e., from the window.onload event handler).

When calling the method, the format string can include standard substitutions (such as '%s') for arguments. This saves you the trouble of calling String.format() yourself. In any case, the formatting JavaScript is added to the script block.

Injecting RenderSupport

Main Article: Environmental Services

RenderSupport is an environmental object, so you will normally inject it via the @Environmental annotation:

  @Environmental
  private RenderSupport renderSupport;

The @Environmental annotation only works inside components, but occasionally a service may want to inject RenderSupport. Fortunately, a proxy of RenderSupport has been set up. The upshot of which is, you may also:

  @Inject
  private RenderSupport renderSupport;

... or, in a service implementation constructor:

  public MyServiceImpl(RenderSupport support)
  {
    . . .
  }

Inside a component, you should use @Environmental, to highlight the fact that RenderSupport (like most environmental objects) is only available during rendering, not during action requests.

@Import Annotation

Added in 5.2.0

In Tapestry 5.2.0 and later, use the @Import annotation to include links to JavaScript (and CSS) files in your page. Tapestry ensures that each such file is only referenced once in your page.

@Import(library="${tapestry.scriptaculous}/dragdrop.js")
public class MyComponent
{
 . . .
}

@Import may also be applied to individual methods, in which case the import operation only occurs when the method is invoked.

Note: When specifying a file to import, you'll often use the prefix context: to indicate that the file is stored in the web application context, and not on the classpath. Relative paths will be on the classpath, relative to the Java class.

@IncludeJavaScriptLibrary Annotation

Deprecated since 5.2 (use @Import instead)

In Tapestry 5.0 and 5.1, use the @IncludeJavaScriptLibrary annotation to JavaScript libraries in your page.

@IncludeJavaScriptLibrary("${tapestry.scriptaculous}/dragdrop.js")
public class MyComponent
{
 . . .
}

This saves you the effort of injecting the asset and making the call to RenderSupport. Chances are you will still inject RenderSupport so as to add some initialization JavaScript.

Combining Scripts

In production mode, Tapestry automatically combines JavaScript libraries. A single request (for a virtual asset) will retrieve the combined content of all referenced JavaScript library files.

This is a very useful feature, as it reduces the number of requests needed to present a page to the user.

As elsewhere, if the client browser supports gzip compression, the combined JavaScript will be compressed.

Client-side Logging

Tapestry uses a modified version of the Blackbird JavaScript console. The Tapestry object includes three functions: debug, warn and error.

Each of these functions take a message and an optional pattern; if the pattern is provided, the message is interpolated on the pattern. The final message is displayed in the Blackbird console, which will make itself visible automatically.

In production mode, debug messages will be filtered out (they will not be visible until the user presses F2 to display the console, and then clicks the grayed out icon for debug messages). In development mode, debug messages are not filtered out.

Example usage:


 Tapestry.debug("Field id is #{id}, value is #{value}", field);

 Tapestry.error("Server is not available.");

Handling Slow Page Loads

If your page loads slowly (typically, because of scripts loaded from external sites), you may see a race condition where the user can click on a link before an event handler for that link has been wired up.

The client-side function Tapestry.waitForPage() can be used in an element's onclick handler to force a wait for the page to fully load. In this race condition, the screen will dim and a message will appear advising the user to wait a moment; once the page is fully loaded, this modal dialog will be removed.

The correct usage is:

  <a href="..." onclick="javascript:Tapestry.waitForPage(event);"> ... </a>

The constant MarkupConstants.WAIT_FOR_PAGE contains this snippet.

The Standard Tapestry Library

Tapestry's client-side support, the standard Tapestry library, is the file tapestry.js, which has dependencies on Prototype and on Scriptaculous Effects. tapestry.js, along with its dependencies, are automatically added to the page when your code adds any other JavaScript or JavaScript library.

Tapestry Namespace

Tapestry defines a number of object and classes inside the Tapestry namespace.

It also adds a handful of methods to the Form class, and to Form elements. These are mostly related to input validation and determining element visibility.

The Tapestry Object

The standard library adds a new function, $T(). This function is used much like Prototype's $(), except that instead of returning a DOM object, it returns a hash (an initially empty JavaScript object) that is associated with the DOM object. This hash is known as the Tapestry object.

You may pass in an object id (as a string) or an object reference. The Tapestry Object is created on first invocation. Note: you'll see it as a property name _tapestry on the DOM object (which may be useful when debugging).

When Tapestry adds information to a DOM object, it does so in the Tapestry object. This helps avoid name conflicts, and groups all Tapestry-added properties into one place which is much easier to debug.

For example, you might store a value for an element in one place:

  $T(myid).fadeDuration = .5;

Then use it somewhere else:

  new Effect.Fade($(myId), { duration: $T(myid).fadeDuration });

Ajax Components and Mixins

Tapestry provides easy-to-use support for Ajax, the technique of using JavaScript to dynamically updating parts of a web page with content from the server without redrawing the whole page. See Ajax and Zones for details.

  • No labels