It requires more than writing the code out to the page using a
ResponseWriter
. For example, you must determine if the script is already on the page.The code I found was part of the
com.sun.faces.renderkit.RenderKitUtils
in a method called renderJsfJs
. This is part of the JSF RI Implementation (jsf-impl). It is dual licensed with GPLv2, or CDDL so please check the license out from the original source.Here is the abbreviated code for the method. It is well done and demonstrates not only how to add the Javascript, but architecturally how to check if it is already rendered, and installed. It also demonstrates how to use a
ResourceHandler
and Resource
. This is by no means the only way, but it does provide a good example.public static void renderJsfJs(FacesContext context) throws IOException { if (hasScriptBeenRendered(context)) { // Already included, return return; } final String name = "jsf.js"; final String library = "javax.faces"; if (hasResourceBeenInstalled(context, name, library)) { setScriptAsRendered(context); return; } // Since we've now determined that it's not in the page, we need to add it. ResourceHandler handler = context.getApplication().getResourceHandler(); Resource resource = handler.createResource(name, library); ResponseWriter writer = context.getResponseWriter(); writer.write('\n'); writer.startElement("script", null); writer.writeAttribute("type", "text/javascript", null); writer.writeAttribute("src", ((resource != null) ? resource.getRequestPath() : ""), null); writer.endElement("script"); writer.append('\r'); writer.append('\n'); // Set the context to record script as included setScriptAsRendered(context); }
The code to determine if the code is installed is just as important as rendering it.
public static boolean hasResourceBeenInstalled(FacesContext ctx, String name, String library) { UIViewRoot viewRoot = ctx.getViewRoot(); ListIterator iter = (viewRoot.getComponentResources(ctx, "head")).listIterator(); while (iter.hasNext()) { UIComponent resource = (UIComponent)iter.next(); String rname = (String)resource.getAttributes().get("name"); String rlibrary = (String)resource.getAttributes().get("library"); if (name.equals(rname) && library.equals(rlibrary)) { // Set the context to record script as included return true; } } iter = (viewRoot.getComponentResources(ctx, "body")).listIterator(); while (iter.hasNext()) { UIComponent resource = (UIComponent)iter.next(); String rname = (String)resource.getAttributes().get("name"); String rlibrary = (String)resource.getAttributes().get("library"); if (name.equals(rname) && library.equals(rlibrary)) { // Set the context to record script as included return true; } } iter = (viewRoot.getComponentResources(ctx, "form")).listIterator(); while (iter.hasNext()) { UIComponent resource = (UIComponent)iter.next(); String rname = (String)resource.getAttributes().get("name"); String rlibrary = (String)resource.getAttributes().get("library"); if (name.equals(rname) && library.equals(rlibrary)) { // Set the context to record script as included return true; } } return false; }
Good tip for those who have to deal with JSF.
ReplyDeleteIt is also a perfect example of why I'm not using JSF - the complexity of dealing w/ it is just overwhelming. Why couldn't this framework (that was supposed to be the mother of all web frameworks) bake this functionality out of the box (e.g just like Tapestry does - http://tapestry.apache.org/javascript.html) ?
Perhaps you missed the point in the article. This is a mechanism for adding in a framework, or Javascript into a custom component. There are a number of frameworks like Mojarra, and PrimeFaces which have Javascript baked into them already like Tapestry.
ReplyDeleteIf you are injecting resources like Javascript you can use the @Resources annotation similar to Tapestry. The scripting still needs to be bound in Tapestry like JSF.
The one thing I like in the code I posted is that it checks to see if the resource has already been included in the page.
Anyway, thanks for the comment. I like Tapestry. I just like JSF more. I found I was more productive with it, and it is a standard with multiple implementations.
That being said, I would recommend that any sensible developer take a look a few frameworks before and try them out. Choose the one that makes you productive, and achieves the objectives you are after.
Thanks a lot for this helpful snippet!
ReplyDelete