Pages

Wednesday, August 01, 2012

JSF Tip of the Day: Adding Javascript to a Custom Component

I was looking through the JSF API and Implementation code, and found a gem that really demonstrates how to add a Javascript framework, or custom Javascript code to your custom JSF component.

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;

    }

3 comments :