My favorites | Sign in
Project Logo
                
Search
for
Updated May 29, 2009 by dobesv
Labels: Featured, Phase-Implementation
TemplateSystem  
How to use the template system

Kiyaa XHTML Templates

Kiyaa templates are based on the facelets template system, and due credit goes to the inventor of facelets for the main ideas behind this. I just take their great ideas and replaced the JSF components with GWT widgets and a new concept called a View which is somewhat similar to the JSF components.

Start With the View Class and Data Model

Each template is associated with a view class that defines the actions and variables available to that template. The view class will have fields for the controller of the application and any model objects which are displayed or edited by the view.

package com.example;
import com.habitsoft.kiyaa.views.GeneratedHTMLView;
import com.google.gwt.core.client.GWT;
/** 
 * Sample XHTML view.
 *
 * The filename of the template, by default, is MyTemplatedView.xhtml in the same
 * package folder as this class.
 */
public abstract class MyTemplatedView implements GeneratedHTMLView {
    /** An imaginary Constants subclass for localization */
    protected final MyLabels labels = GWT.create(MyLabels.class);
    protected MyLabels getLabels() { return labels; }

    /** An imaginary controller class that implements our business logic */
    protected final MyController controller = MyController.getInstance();
    protected MyController getController() { return controller; }

    /** Some field we'll let the UI mess with */
    int height;
    protected int getHeight() { return height; }
    protected void setHeight(int height) { this.height = height; }
    protected void checkHeight() { controller.checkHeight(height); }

    /** Example static factory method to illustrate how these are created */
    public static final MyTemplatedView createInstance() {
        return GWT.create(MyTemplatedView.class);
    }

}

Notes:

Basic XHTML Example

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:k="http://habitsoft.com/kiyaa/core"
      xmlns:ui="http://habitsoft.com/kiyaa/ui"
      xmlns:gwt="http://habitsoft.com/kiyaa/gwt">
    <body> 
        This text is removed
        <k:view class="account-editor">
            <h1>${labels.exampleTitle}</h1>
            <h2>Non-localized Text Using a Title Tag</h2>
            <ui:label class="my-label">
                  ${labels.thisIsAnExampleOfALabel}
            </ui:label>

            <label>Enter your height in cm:
            <ui:textbox class="my-textbox" text="#{height}"/>
            </label>
             
            <!-- Button which calls methods directly on the controller -->
            <ui:button onclick="controller.checkHeight(height)" text="Button 1"/>

            <!-- Button which calls methods on the view class 
                 (probably a better convention, but more work) -->
            <ui:button onclick="checkHeight" text="${labels.button2}"/>

            Localization can be supported inside text using
            ${labels.interpolations}.  Note however, that these
            are often wrapped in a span element.
        </k:view>
	 
	This text is removed
    </body>
</html>

Notes:

The EL Syntax

As you've seen by now the expressions have 3 formats:

  1. ${ ... } contains an expression which is read when the view loads and copied into the target property on the widget
  2. #{ ... } extends the behavior of ${ ... } by additionally copying the value back out of the widget to the given expression when the view saves.
  3. %{ ... } is a variation of ${ ... } that only runs once, when the view is created. Use this for constant values to avoid re-setting them on every load
  4. @{ ... } is a variation of ${ ... } that loads the value "early" - that is, values with @{ ... } are set before values with ${ ... }. Use this for the condition of k:when to get the view to show up without waiting for other parts of the view to load.

But wait ... there's more! Here are some more of the features supported:

Some pitfalls:

Actions

Specific attributes, as well as some widget or view properties are considered to be "actions" and get special treatment. Actions are identified as follows:

You can usually get more information about the event (like onKeyPress) from Event.getCurrentEvent(), as long as the save() implementation for your view completes without returning to the main event loop.

Action Syntax

An action can be the name of a method to call, for example:

onclick='sayHello()'

The method can take parameters, and the parameters may be expressions or even asynchronous properties:

onclick='sayHello(labels.helloWorld)'

You can also chain multiple actions together using semicolons, they will be run in order until the end, or one of them fails:

onclick="sayHello() ; sayGoodbye()"

You can use assignments as part of an action to set a property:

onclick='name = "Bob" ; sayHello(name)'

You can also define an empty action to simply refresh the view:

onclick=''

Or use the word 'null' as a no-op:

onclick='null'

Putting a semicolon at the beginning of an action tells kiyaa NOT to save the view before running the action, and a semicolon at the end indicates that load should NOT be run after the action:

onclick='; doSomethingWithoutRefreshingTheView() ;'

To refresh a different view than the one currently being defined, use the "on view: ..." prefix:

onclick='on parentView: doSomethingThatAffectsDataInTheParentView'
onclick='on childView: doSomethingThatOnlyAffectsDataInTheChildView'

Defining Views inside Panels

Any View or Widget which implements a method setView, addView, setWidget, or setViewFactory taking a View, 'Widget', or ViewFactory as a parameter (matching the method name) can have an inner subview which is also defined in the same xhtml file. The generator will construct another View subclass and pass either an instance of it, the result of its getViewWidget() method, or an instance of ViewFactory which constructs and returns it. This is very useful for defining panels which can contain other views.

When creating a View or a ViewFactory, the special XML attribute 'with-model' indicates that the view is a ModelView. This property contains a fully qualified class name followed by a variable name which is bound to the "model" of the view; that is, when someone calls setModel on the ModelView the value is stored into a variable with the given name. This variable can only be accessed inside the element where it is defined.

See the list and table sections below for examples.

Conditional Display

It's generally quite useful to be able to show and hide parts of the view based on the state of the application. The template system provides two ways of handling this; first, you can call the setVisible method of any widget supporting it by using the visible attribute:

<gwt:checkbox visible="${shouldShowCheckbox}"/>

In addition, a special WhenView class is provided, which is typically accessed using the tag k:when. This view takes a test property and a viewFactory, which can be created automatically for the elements inside it; for example:

<k:when test="${myEntity != null}">
   <ui:label class="entity-name-label" text="${myEntity.name}"/>
</k:when>
<k:when test="${myEntity == null}">
   <h1>Entity is NULL!  NNnnnooOOoooOOoooo!!!!</h1>
</k:when>

The condition passed to test must be enclosed in ${ ... } and may use boolean operators like and, or, <, >, <=, >=, != along with method calls (foo.bar(baz)), bean property paths (foo.bar.baz), arithmetic, and various combinations thereof. Note: these operators are not specific to k:when and can be used in any EL expression; I'm mentioning them here for convenience.

WhenView is given a ViewFactory and it only constructs the inner view if the condition is true as a kind of Lazy Panel. This means that the code inside WhenView can be somewhat expensive to run, and also that it can assume that the condition is true in its code since it won't be invoked otherwise.

Lists and Tables

Lists are implemented using ListView, whose tag is ui:list. The list of objects is set using the models property, which should be given an array of objects. For each object in the array, a View is constructed using - you guessed it - a ViewFactory. The ViewFactory must return a ModelView subclass which provides the setModel method used by the list to tell each view which object it is displaying.

For example:

<h2>Here are some strings:</h2>
<ui:list models="${someStringArray}" with-model="java.lang.String someString">
    <ui:label>${"The string is: "+someString}</label>
</ui:list>
<h2>Here's some more information about those strings in a table:</h2>
<ui:table models="${someStringArray}" with-model="java.lang.String someString">
    <ui:column heading="Length">
        <ui:label>${someString.length}</ui:label>
    </ui:column>
    <ui:column heading="Content">
        ${someString}
    </ui:column>
    <ui:column heading="HashCode">
        # ${someString.hashCode}
    </ui:column>
</ui:table>

In addition to the heading, the columns can also have a class to set the css class for cells in the column and/or a test to make the entire column conditionally visible (using setVisible()).

The HTML structure of the table is designed to be styled using CSS, and the DOM tree ends up looking like this:

<div class="ui-table"> <!-- change this CSS class using `class` or `styleName` -->
<!-- Optional ui:navigation if specified, shows up here in a DIV -->
<div class="table-border-top"/>
<div class="table-border-middle">
<table class="ui-table"> <!-- change this CSS class using `tableClass` -->
<thead><!-- headings ---></thead>
<tbody>
<!-- each row has ui-table-row and either odd or even -->
<tr class="ui-table-row odd">
    <td> <!-- td will have the class given in the ui:column element --> </td>
</tr>
</tbody>
</table>
<!-- Optional ui:emptyContent if specified and the table is empty,
     shows up here in a DIV if the table is empty -->
<div class="empty-table-content" style="display: none;"> Empty table content </div>
</div>
<div class="table-border-bottom"/>
</div>

The list can also be styled using css

<!-- The css class of the list is ui-list by default, the -selectable and
     -clickable suffixes are added if you set selectable=true or 
     clickable=true on the list.  Change the base "ui-list" by setting
     class or styleName. -->
<div class="ui-list ui-list-selectable ui-list-clickable">
<!-- Each list item gets a div with the class ui-list-item, and an odd or
     even depending on which row it is in. -->
<div class="ui-list-item odd">
</div>
</div>

Lists and tables can also be "clickable" and/or "selectable" in which case they'll start to listen for hover and click events.

Example:

<ui:list id="employeeList"
         models="${employees}"
         clickable="true"
         onclick="showEmployee(employeeList.selectedModel)">
    ${employee.name}
</ui:list>

Tables also have some other features, like context menus, which I haven't even gotten into yet and are still at a somewhat experimental stage. Look at the source to "discover" some more handy features.

Widget Bindings

When a widget or view in the template has the special attribute binding it will be set into the view when it is created. For example:

   /** TextBox field in our view class we want to bind to */
   private TextBox myTextBox;
   protected TextBox getMyTextBox() { return myTextBox; }
   protected void setMyTextBox(TextBox tb) { this.myTextBox = tb; }

   /** Some example action that makes use of the binding for something */
   protected void tbChanged() { tb.setStyleName("dirty", true); }
<k:view>
   <!-- By specifying the binding property here, we can get 
        direct access to this textbox in the code -->
   <ui:textbox binding="myTextBox" text="#{height}"/>
<k:view>

When you want to refer to a widget from an action, but don't want to bother with a binding, you can use the id attribute instead. Any widget or view with an id attribute is declared in such a way that it is accessible to its sibling or child views and widgets. Example:

   <h1>Click the button to change the textbox</h1>
   <ui:textbox id="tb1" text="Initial Value"/><br/>
   <ui:button onclick='tb1.text = "new value"'/><br/>

Loading and Saving

The interface for View defines operations load() and save().

You can define your own methods load() and save() and the subclass will call them before their own implementation, and only if they return success to the callback. This allows you to pre-load some data from the server which is used in the view and which you don't want to be re-loading several times while the view is loaded - the view is not very smart about sharing the results of previous

Debugging Template Issues

GWT stores the generated files on disk in the build.gwt/.gen folder (or something like that, depending on your GWT setup) so if you get compile errors or stack traces passing through those files, you'll often have to look at the generated output to figure out what's going wrong. Often you'll have something that is null, but you can't tell what until you look at the right line number in the generated code.

The generator is implemented in com.habitsoft.kiyaa.rebind.GeneratedHTMLViewGenerator, if you run into problems where the generated code is invalid, buggy, or doesn't compile you'll have to look there.

Adding Tag Libraries

You can of course define your own tag libraries as namespaces - the libraries shown in the example are the ones included. The files mus be placed in the classpath in the META-INF folder, and named with the extension .kiyaa-taglib.xml. Here's an example:

<kiyaa-taglib>
    <!--
	This tag library contains GWT widgets that can be instantiated
	directly using the already available attributes. 
    -->
    <namespace>http://habitsoft.com/kiyaa/gwt</namespace>
    <package>com.google.gwt.user.client.ui</package>
    <tag>
	<tag-name>textbox</tag-name>
	<tag-class>TextBox</tag-class>
	<content-attr>text</content-attr>
    </tag>
    <tag>
	<tag-name>label</tag-name>
	<tag-class>Label</tag-class>
	<content-attribute>text</content-attribute>
    </tag>
    <tag>
        <tag-name>a</tag-name>
        <tag-class>Anchor</tag-class>
        <default for="href">javascript:void(0)</default>
        <content-attribute>text</content-attribute>
    </tag>
    <tag>
        <tag-name>form</tag-name>
        <tag-class>FormPanel</tag-class>
        <default for="encoding">multipart/form-data</default>
        <default for="method">post</default>
    </tag>
    <tag>
        <tag-name>file-upload</tag-name>
        <tag-class>FileUpload</tag-class>
        <default for="name">data</default>
    </tag>
    <tag>
        <tag-name>hidden</tag-name>
        <tag-class>Hidden</tag-class>
        <content-attribute>value</content-attribute>
    </tag>
</kiyaa-taglib>

Notes:

More information

There are some javadocs at the top of the GeneratedHTMLViewGenerator class with more information (sorry I didn't copy it all in here yet ...). Furthermore, that class itself represents althmost the entire implementation of this templating system, and thus stands as the ultimate reference of what can be done, as long as you can grok the code (it's not heavily commented).


Comment by shashankajoshi, Dec 04, 2008

Cool! any more examples something like the showcase built using kiyaa would be gr8.

Comment by shashankajoshi, Jan 12, 2009

I have implemented custom components in kiyaa now for Widgets button, tree etc. i am moving on to containers. Idea is to allow templated ui developments using kiyaa and gwt ext libraries. progress is slow due to other commitments. but will keep you posted.

Comment by dobesv, May 29, 2009

Note the addition of %{ ... } for costants to improve performance, and the "on parentView: ..." action syntax to allow your action to refresh another view instead of the current one when it runs.

Comment by andyhine, Oct 13, 2009

Could you provide a downloadable example project of the above?

Thanks


Sign in to add a comment
Hosted by Google Code