Friday, July 13, 2012

Programmatic facelet: resource to component

Facelet is a great way to write your UI layout in xml declaratively. However, in highly dynamically website, you need to control facelet construction programmatically.

This seems silly since the the purpose of the facelet to describe the component layout. If you use programmatic approach, you can program  component tree directly.  This is not completely true. Facelet has the concept of component, composition and template. I may define a fragment of tree structure as component or template and want to mix them programmatically. I do not need to program everything from scratch.

I will start a series article to demonstrate how you can do this. I assume JSF 2.2 is used. You can download the JSF reference implementation here:http://javaserverfaces.java.net/nonav/rlnotes/2.2.0-m01/

Suppose you have a RESOURCE like this
 WebContent/resources/test/fragment.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
</h:head>
<h:body>
<ui:component>
This is a fragment
</ui:component>
</h:body>
</html>
 

You can add this component to your tree structure like this

public  Facelet resouceToFacet(String resource)
    {
        FacesContext fctx = FacesContext.getCurrentInstance();
        Application app = fctx.getApplication();
        FaceletFactory faceletFactory = (FaceletFactory) FactoryFinder
                .getFactory(FactoryFinder.FACELET_FACTORY);
        Resource res = app.getResourceHandler().createResource(resource);
        Facelet f;
        try
        {
            f = faceletFactory.getFacelet(res.getURL());
        } catch (IOException e)
        {
            throw new RuntimeException(e);
        }
        return f;

    }

public static UIComponent faceletToComponent(Facelet f)
    {
        FacesContext fctx = FacesContext.getCurrentInstance();
        Application app = fctx.getApplication();
        UIPanel panel = (UIPanel) app.createComponent(UIPanel.COMPONENT_TYPE);
        try
        {
            f.apply(fctx, panel);
        } catch (IOException e)
        {
            throw new RuntimeException(e);
        }
        UIComponent newComponent = panel.getChildren().remove(0);

        return newComponent;
    }

faceletToComponent(resouceToFacet("test/fragment.xhtml"));


Leaving fragment.xhtml as a resource gives us the advantage to use ResourceHandler to find the file. If it is not a resource, we need to find the URL for this file. We could do it like this
new File(FacesContext.getCurrentInstance().getExternalContext().getRealPath(PATH) ).toURI().toURL();
Once we have the URL for the fragment file, we can create a facelet from it using FaceletFactory. The created facelet can then be converted to UIComponent.







No comments:

Post a Comment