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 {The implementation of the Interface could looks like the following block. Don't forget to set the scope to perthread.
/**
* 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 ListgetScriptFiles();
/**
* Returns a list with scripts to append. Needs to be a list to preserve order of scripts.
*
* @return list with scripts to append.
*/
public ListgetScripts();
}
@Scope(value = ScopeConstants.PERTHREAD)
public class AppendedScriptsService implements IAppendedScriptsService {
private final ListscriptFiles = new ArrayList ();
private final Listscripts = new ArrayList ();
@Override
public void addScriptFile(String scriptFile) {
this.scriptFiles.add(scriptFile);
}
@Override
public void addScript(String script) {
this.scripts.add(script);
}
@Override
public ListgetScriptFiles() {
return this.scriptFiles;
}
@Override
public ListgetScripts() {
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)
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) {User Component
binder.bind(IAppendedScriptsService.class, AppendedScriptsService.class);
}
To use this Component, you just have to add this into a class of a page or component:
@Inject
private IAppendedScriptsService appendedScriptsService;
The order of scripts and script files is preserved.
/**
* Append JavaScripts
**/
void setupRender() {
this.appendedScriptsService.addScriptFile("UrlToScript");
appendedScriptsService.addScript("var x = 'cool';");
}
Do you have any comments? Let's discuss this in the Nabble Forum. I posted the Topic: "Append JavaScript at the end of my html file" - Component for Tapestry 5.1
EDIT: Howard Lewis Ship commented in the Nabble Forum:
The correct approach is to modify the DocumentLinker service (which is internal) to place the
Keine Kommentare:
Kommentar veröffentlichen