You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by janp <na...@jmehl.de> on 2010/10/21 10:52:37 UTC

"Append JavaScript at the end of my html file" - Component for Tapestry 5.1

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: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Re: "Append JavaScript at the end of my html file" - Component for Tapestry 5.1

Posted by janp <na...@jmehl.de>.
Thank you for that hint. I started to investigate how to change it in the
DocumentLinker Service.

If I understand the JavaDoc right, it is still in the head tag in the
current 5.2, but in the SNAPSHOT, it is configurable. So it will be
configurable again in the final 5.2 release?
-- 
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-tp3230082p3238847.html
Sent from the Tapestry - Dev mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Re: "Append JavaScript at the end of my html file" - Component for Tapestry 5.1

Posted by Howard Lewis Ship <hl...@gmail.com>.
The correct approach is to modify the DocumentLinker service (which is
internal) to place the <script> tags where you want.

On Thu, Oct 21, 2010 at 1:52 AM, janp <na...@jmehl.de> wrote:
>
> 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: dev-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: dev-help@tapestry.apache.org
>
>



-- 
Howard M. Lewis Ship

Creator of Apache Tapestry

The source for Tapestry training, mentoring and support. Contact me to
learn how I can get you up and productive in Tapestry fast!

(971) 678-5210
http://howardlewisship.com

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org