This post describes a service, that enables Tapestry to append JavaScript files and script parts at the end of a html file. Script files and scripts can be "dropped into the service" from any page class or component and will be rendered in that order (FIFO) at the end of the page.
Upgrade Tapestry 5.0.x to Tapestry 5.1. loads included JavaScript files in the HTML head. Reasons are discussed in the Upgrade Notes: http://tapestry.apache.org/tapestry5.1/upgrade.html project-wide release notes. They told me that in fix for bug TAP5-544: Firstly, JavaScript libraries would have to move (back) to the head, permanently, no configuration [...]. Maybe I did not understand the real concern, but: Why isn't it up to the developer to decide where to place script files? I decided to write a component that appends my script files at the end of my html files. The most important reason for this descicion is, that--following Yahoo's performance guidelines--I'd like to first load my HTML DOM before loading the script files. Write a Service to Collect Files to Append We need an Interface to be able to use the perthread scope (more about scoypes: "a per-thread instance is created on demand, behind a shared proxy"). The Interface just consists of 4 methods. 2 to add Script files and Scripts to the list and 2 to get the lists when they should be rendered: public interface IAppendedScriptsService { /** * Add a script file to the list of script files to append. * * @param scriptFile */ public void addScriptFile(String scriptFile); /** * Add a script to the list of scripts to append. * * @param script */ public void addScript(String script); /** * Returns a list with script files to append. Needs to be a list to preserve order of script files. * * @return list with script files to append. */ public List getScriptFiles(); /** * Returns a list with scripts to append. Needs to be a list to preserve order of scripts. * * @return list with scripts to append. */ public List getScripts(); } The implementation of the Interface could looks like the following block. Don't forget to set the scope to perthread. @Scope(value = ScopeConstants.PERTHREAD) public class AppendedScriptsService implements IAppendedScriptsService { private final List scriptFiles = new ArrayList(); private final List scripts = new ArrayList(); @Override public void addScriptFile(String scriptFile) { this.scriptFiles.add(scriptFile); } @Override public void addScript(String script) { this.scripts.add(script); } @Override public List getScriptFiles() { return this.scriptFiles; } @Override public List getScripts() { return this.scripts; } } Build the Component The Component was easy to write. It consists of just one method to render the scripts: // Inject the service we just wrote. @Inject private IAppendedScriptsService appendedScriptsService; // This method is called when component should be rendered. (Markup writer infos here) void beginRender(MarkupWriter markupWriter) { // Output script files. if (!this.appendedScriptsService.getScriptFiles().isEmpty()) { for (final String file : this.appendedScriptsService.getScriptFiles()) { // There might be another (tapestry conform way) to create the source link. My way is String fileUrl = "MyAssetsServerURL".resolveRelative(file); markupWriter.element("script", "type", "text/javascript", "src", fileUrl); markupWriter.end(); } } // Output script snippets. if (!this.appendedScriptsService.getScripts().isEmpty()) { for (final String script : this.appendedScriptsService.getScripts()) { markupWriter.element("script", "type", "text/javascript"); markupWriter.getElement().text(script); markupWriter.end(); } } } That's all to implement. Just one last step to make this run: Activate Service in AppModule To activate your Service, following method must be inserted into AppModule.java (or just insert the binder-line if there is already such a method): public static void bind(ServiceBinder binder) { binder.bind(IAppendedScriptsService.class, AppendedScriptsService.class); } User Component To use this Component, you just have to add this into a class of a page or component: @Inject private IAppendedScriptsService appendedScriptsService; /** * Append JavaScripts **/ void setupRender() { this.appendedScriptsService.addScriptFile("UrlToScript"); appendedScriptsService.addScript("var x = 'cool';"); } The order of scripts and script files is preserved. Do you have any comments? Please, let's discuss this. -- View this message in context: http://tapestry.1045711.n5.nabble.com/Append-JavaScript-at-the-end-of-my-html-file-Component-for-Tapestry-5-1-tp3230082p3230082.html Sent from the Tapestry - Dev mailing list archive at Nabble.com. --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
