Custom Component Renderers and Updaters

AjaxSwing renderers and updaters can be used to support custom components and to override HTML generation for standard components (java.awt.* and javax.swing.*). Only one custom renderer per application is allowed in the Standard Edition, and if you need more renderers you need to purchase an appropriate commercial license. AjaxSwing maintains a map of components and their renderers. When iterating children of a container generate HTML page AjaxSwing first determines the name of the class of each child component. Then it tries to lookup the renderer for this component in the map of renderers. If a renderer is found, it is instantiated and used, otherwise the superclass of the component is checked for a registered renderer. AjaxSwing has an universal SnapshotRenderer that can render any component as an image on a page. 

Supporting custom GUI components requires creating a Freemarker template or coding and registering a renderer class Creating a template is easier and more straightforward, so it is a good choice for components that map cleanly to HTML code. Developing a renderer class is a better choice in cases where a lot of logic needs to be evaluated or calculated to produce HTML. Renderer class is also faster and more maintainable because it doesn't rely on reflection like the Freemarker template. Finally, both approaches can be combined by extending com.creamtec.ajaxswing.v4.render.TemplateRenderer and preparing extra component-specific data for the template.

AjaxSwing requires that a renderer produces a self-contained single HTML element with component ID that may include HTML and JavaScript child elements. This requirement allows AjaxSwing to uniquely identify the HTML fragment that corresponds to the component. This fragment is used to compute the hash for state comparison, and if the fragment is changed from the previous AJAX request the new fragment is sent to the browser as a partial update. For instance, if you are rendering some text and some JavaScript to go with it, the generated HTML structure should look like this:

<div class="JLabel" id="JLabel_6436242">
  <div class="htmlString" style="position:absolute;left:2px;top:15px;width:46px;white-space:nowrap;text-align:left;color:#333333">
      Text
  </div>
  <script type="text/javaScript" defer>$(function(){
    document.title = 'Custom Control example';
    });
  </script>
</div>

Custom component rendering using a template (similar to JSP)

Another alternative to supporting custom component rendering is to create a rendering template. This approach doesn't require developing Java classes and is similar to writing a JSP page. AjaxSwing uses Freemarker templating engine for processing templates. The templates must be placed into AjaxSwing/conf/templates/custom-components directory and the name of the template must follow .html pattern. For example, if AjaxSwing is installed into C:/AjaxSwing directory and the custom component class is com.creamtec.ajaxswing.ui.CustomPanel, then the template file name should be c:/AjaxSwing/conf/templates/custom-components/com.creamtec.ajaxswing.ui.CustomPanel.html.

Templates syntax must follow Freemarker rules. The following data objects are available to the template:

  • page - currently rendered HTMLPage
  • agent - ClientAgent
  • component - component to be rendered
  • id - id/name of the component to be rendered
  • style - component style attributes derived from Swing component
Note that all methods and properties of objects are available to the template via Freemarker expressions. See AjaxSwing/conf/templates/default-component/com.creamtec.ajaxswing.ui.CustomPanel.html template for an example of container template.

Custom component renderering using a renderer class (similar to a servlet)

Writing renderer is similar to writing a servlet that only produces a portion of a page. All renderers must implement com.creamtec.ajaxswing.v4.render.ComponentRenderer interface. If you look at ComponentRenderer interface, you would see that it is very simple and defines two methods

void initialize()

This method is by AjaxSwing  to initialize renderer. This method is only called once per page creation.

void renderComponent(ComponentGraphics graphics, Component component) throws Exception

This method is called to append the HTML representation of the component to the page. This code can be as simple as <IMG> tag or as complex as <IFRAME></IFRAME> section.

The passed graphics is the output that will be returned to the client. Component is the instance of the component to be rendered. See API JavaDoc and CustomComponentProcessor.java on specifics of what methods to use and required parameters.

 

Registering the custom renderer with AjaxSwing

Once a custom renderer class has been developed, it has to be registered with AjaxSwing. This is done by adding an entry to the application's properties file in the conf directory. The entry follows format <renderer>.<package_name>.<class_name>= <renderer_class_name>. For example, to use our example renderer com.creamtec.ajaxswing.examples.CustomControlProcessor for component com.creamtec.ajaxswing.ui.CustomControl in your application MyFirstApp you will add the following line to <AjaxSwing>/conf/MyFirstApp.properties

renderer.com.creamtec.ajaxswing.ui.CustomControl=com.creamtec.ajaxswing.examples.CustomControlProcessor

Similarly, if you want to use your CustomControlProcessor to render Swing's JSlider you can use

renderer.javax.swing.JSlider=com.creamtec.ajaxswing.examples.CustomControlProcessor

 

Custom Updater

As you have learned, custom renderers allow you to extend AjaxSwing by providing logic to render unsupported controls to HTML page. But what if the control is an input field such as a text field or a radio button? Generating HTML for it will allow the user to see it on the page, but if the user changes the value of the field how would it get propagated back to the UI element such as JTextField? The solution for this is to add a custom ComponentUpdater. Component updaters are used by AjaxSwing engine to set values received from the browser to the UI elements. It can be as simple as calling a setText() on a JTextField and as complicated as synchronizing the JTable model to match the new values received from the HTML table. Just like custom renderers, updaters need to be registered for the controls that they support. For example, the line below tells AjaxSwing to use CustomControlProcessor to update CustomControl

updater.com.creamtec.ajaxswing.ui.CustomControl=com.creamtec.ajaxswing.examples.CustomControlProcessor

Custom updaters must implement ComponentUpdater interface and provide implementation for updateComponent method. The implementation is specific to the control and the rendering that was used to represent it on HTML page. See Javadoc for more information.

Implementation Example for a Custom Control

CustomControlProcessor.java