Employment/Purpose

Here we describe how to embed ZK component(s) as a native element of a foreign framework. For example, how ZK components can be embedded as a native JSF component. It allows application developers to use the native element without knowing the existence of ZK.

For the sake of description, we call it the embedded component.


Note: If it is OK for your developers to work on ZUL directly, it is more convenient and powerful to use the inclusion (such as <jsp:include>) or ZK JSP > Tags, and you don’t have to wrap them into a native element.

Prerequisite

DOCTYPE

To use ZK components correctly, the pages generated by the foreign framework (JSP, JSF…) must generate the doc type as follows.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Browser Cache

Though optional, it is suggested to disable the browser to cache the result page. It can be done as follows.

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Expires" content="-1" />

Embed a component directly

The simplest way to embed is to invoke

org.zkoss.zkplus.embed.Renders

when it is time to output the content of the native element.

For example, if you are implementing a JSP tag, then you can invoke the render method in doTag() as follows.

public void doTag() throws JspException, IOException {
    //prepare variables
    final JspContext jspctx = getJspContext();
    final PageContext pgctx = Jsps.getPageContext(jspctx);
    final ServletContext svlctx = pgctx.getServletContext();
    final HttpServletRequest request = (HttpServletRequest)pgctx.getRequest();
    final HttpServletResponse response = (HttpServletResponse)pgctx.getResponse();

    //create components
    Listbox listbox = new Listbox();
    listbox.appendChild(new Listitem("Item 1"));
    listbox.appendChild(new Listitem("Item 2"));

    //render the result
    final StringWriter out = new StringWriter();
    Renders.render(svlctx, request, response, listbox, null, out);
    getJspBody().invoke(out);
}

Embed by implementing a richlet

If you want to have more control, such as applying a composer provided by users or creating components from a ZUL page, you could implement a richlet (org.zkoss.zk.ui.Richlet) and then invoke

org.zkoss.zkplus.embed.Renders

instead.

Renders.render(svlctx, request, response,
  new GenericRichlet() {
    public void service(Page page) throws Exception {
        //execution is ready
        //... do whatever you want
        Window main = new Window();
        main.setPage(page); //associate to the page
        Executions.createComponents("/WEB-INF/template/foo.zul", main, null);
        composer.doAfterCompose(main); //assume user assigned a composer
    }
  }, null, out);

where we use org.zkoss.zk.ui.GenericRichlet to simplify the implementation of a richlet.

Example

Embed as a native JSF component

ZK Component as a native JSF component can be easily achieved by wrapping it as a custom JSF component 1 and rendering it in Render Response Phase of JSF lifecycle by invoking

org.zkoss.zkplus.embed.Renders
@FacesComponent(value = "window")
public class WindowTag extends UIComponentBase {
    private static final Log log = Log.lookup(WindowTag.class);
    private Window window;
    
    public void encodeBegin(FacesContext context) throws IOException {
        ServletContext svlctx = (ServletContext)context.getExternalContext().getContext();
        HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
        HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
        ResponseWriter responseWriter = context.getResponseWriter();

        try {
            Renders.render(svlctx, request,response, 
                    new GenericRichlet() {  
                        public void service(Page page) throws Exception {
                            window = new Window();
                            window.setPage(page);
                            applyProperties();
                            doAfterCompose();
                        }
                    }, null, responseWriter);
        } catch (ServletException e) {
            throw new IOException(e.getMessage());
        } 
    }

    /** apply ZK component properties as retrieved from JSF custom component tag */
    private void applyProperties() {
        Map<String, Object> attrs = getAttributes();
        Set<String> attrNames = attrs.keySet();
        
        for (Iterator iterator = attrNames.iterator(); iterator.hasNext();) {
            String attrName = (String) iterator.next();
            if(!"apply".equals(attrName)) {
                try {
                    Property.assign(window, attrName, attrs.get(attrName).toString());
                } catch(PropertyNotFoundException pnfe) {
                    log.debug(pnfe.getMessage());
                }
            }
        }
    }
    /** apply composer by calling doAfterCompose after ZK component is composed */
    private void doAfterCompose() throws Exception {
        Object o = getAttributes().get("apply");
        if(o instanceof String) {
            o = Classes.newInstanceByThread(o.toString());
        }
        if(o instanceof Composer) {
            ((Composer)o).doAfterCompose(window);
        }
    }

....
}

Version History

Version Date Content
5.0.5 September 2010 org.zkoss.zkplus.embed.Renders was introduced to simplify the making of a native element for a foreign framework.