|
HtmlTemplateFactory
MotivationCurrently GWT does not provide a mechanism to easily glue pre existing elements to widget instances. It also does not include functionality to embed html, text files or execute templates on the client. In order to achieve the later one can:
Goals
Non Goals
HtmlTemplateFactoryThe rocket.widget.client.HtmlTemplateFactory factory makes it easy to bind existing elements identified by an id to a widget by defining an interface with a number of getters along with annotations to specify the id. The templating problem discribed above is also supported via annotations. Getting StartedImport the rocket.widget.Widget module. Binding an element with id to a widgetAn exampleThe html page below represents a simple logon page. An example below describes how to prepare a interface that will bind the three elements below to a TextBox, Password and Button widgets. <html> <body> ... <form> Login: <input type="text" id="username"/> Password: <input type="password" id="password"/> <button id="submit">Login</button> </form> ... </body> </html>
Defining the interfaceThe interface below has been prepared that descibes three methods to get a widget that will be bound to each of the three elements on the page above. package ...
import rocket.widget.client.TextBox;
import rocket.widget.client.Button;
import rocket.widget.client.HtmlTemplateFactory;
public interface HijackedElementFactory extends HtmlTemplateFactory {
/*
* @id username
*/
TextBox getUsername();
/*
* @id password
*/
TextBox getPassword();
/*
* @id submit
*/
Button getSubmit();
}The actual id of the element is noted by including an @id annotation. The getUsername() method has a return type of TextBox and an id of "username". When the interface is realised any calls to it will return the same TextBox which will be bound to the username textbox.
Using the factoryThe deferred binding mechanism is used to realise a class that implements the interface contract described above. The first step involves fetching an instance of the factory using deferred binding passing the class literal of the prepared HijackedElementFactory interface. The user may then call any of the getter methods to fetch the corresponding widget. package ... import com.google.gwt.core.client.GWT; import rocket.widget.client.TextBox; import rocket.widget.client.Button; import rocket.widget.client.HtmlTemplateFactory; ... // inside some method... HijackedElementFactory factory = (HijackedElementFactory) GWT.create( HijackedElementFactory.class ); TextBox username = factory.getUsername(); TextBox password = factory.getPassword(); TextBox submit = factory.getSubmit();
Alternative way of binding elements without using a HtmlTemplateFactoryThe rocket widgets provide an extra constructor that allows a user to pass in any element. The widget will then adopt that element providing its compatible (dont pass Button elements to TextBox widgets). The sample below shows how this can be done. package ... import com.google.gwt.user.client.DOM import rocket.widget.client.TextBox; import rocket.widget.client.Button; ... TextBox username = new TextBox( DOM.getElementById( "username" )); System.out.println( username.isPassword ); // prints false TextBox password = new TextBox( DOM.getElementById( "password" )); System.out.println( password.isPassword ); // prints true Button submit = new Button( DOM.getElementById( "submit" )); System.out.println( submit.getParent() ); // will print RootPanel.
Supported widgetsThe table below includes a mapping between html elements to rocket widgets.
Dont worry even though the password is a TextBox reference the element remains as a password element masking any entered text. From a design perspective both elements have similar methods and fire the same events so it makes sense to have a single class to represent them. The TextBox.isPassword() method may be used to determine if the element is a regular text box or a password. For more info refer to the BasicWidgets Embedding a Html / Template into an ApplicationThe HtmlTemplateFactory also supports a new annotation when the return type of a defined method is Html. The @file annotation may be used to specify a file which will be included within the generated javascript. Any calls to that method will result in a new Html element containing the contents of the file without making any http requests back to the server. The file which has many similarities to a jsp may also contain blocks of java. An exampleThis simple example will show how to create embedd a simple template that contains some java to create the rows of a table. It will not show the definition of the value objects, lets just assume they exist. Defining the interfaceThe interface below has been prepared that descibes three methods to get a widget that will be bound to each of the three elements on the page above. package ...
import rocket.widget.client.TextBox;
import rocket.widget.client.Button;
import rocket.widget.client.HtmlTemplateFactory;
public interface SampleTemplate extends HtmlTemplateFactory {
/*
* @file /template.txt
*/
Html createTableWithRows( RowData[] rows );
}The template fileThe template file is a simple text or html file with blocks of java (<% ...java... %>) or values (specified <%= value/variable %>). The syntax is very similar to that of jsps without the custom tags. Any parameters to the method are available within the template using the same name. <table>
<tr>
<td>Column 1</td><td>Column 2</td><td>Column 3</td>
</tr>
<%
// loop over each row creating a TR for each one.
for( int i = 0; i < rows.length; i++ ){
RowData row = rows[ i ];
%>
<tr>
<td><%= row.getColumn1() %></td><td><%= row.getColumn2() %></td><td><%= row.getColumn3() %></td>
</tr>
<%
} // end of for loop.
%>
</table>
Using the factoryThe deferred binding mechanism is used to realise a class that implements the interface contract described above. The first step involves fetching an instance of the factory using deferred binding passing the class literal of the prepared HijackedElementFactory interface. The user may then call any of the getter methods to fetch the corresponding widget. package ... import com.google.gwt.core.client.GWT; import rocket.widget.client.TextBox; import rocket.widget.client.Button; import rocket.widget.client.HtmlTemplateFactory; ... // inside some method... RowData[] rowData = createRowData(); // this imaginary method creates an array of RowData instances. // create the factory SampleTemplate factory = (SampleTemplate) GWT.create( SampleTemplate.class ); // execute the template. Html tableWithRows = factory.createTableWithRows( rowData ); Further samplesFor further examples refer to the unit test and related demos.
| ||||||||||||||||||||||
Great job! One question - will performance be the same as i would create html page using GWT classes (like new FlowPanel?().add(new TextBox?()... new Button())), or your code generator just analyzes html at runtime ? I'm asking because at first case the code will automatically check presense of 'id' within html, but it is "Not goal". If your code implements second case, i'm worrying about performance...
Few typos:
/ should be replaced to / otherwise, annotations will not worl
2 TextBox? submit = factory.getSubmit();
should be replaced to
1/ The goal of this factory is not to create dom elements but rather to hook up existing dom elements to widget instances. Creating dom elements via GWT will always be slower than a set inner html because more stuff is happening w/ gwt. This mechanism should be thought of more or less as a convenience that allows someone to design some html and then the developer would use this to bind behaviour etc to the html.
2/ thanx for catching the typo.
package ...import com.google.gwt.core.client.GWT;import rocket.widget.client.TextBox?;import rocket.widget.client.Button;import rocket.widget.client.HtmlTemplateFactory;... // inside some method... RowData? rowData = createRowData(); // this imaginary method creates an array of RowData? instances. // create the factory SampleTemplate? factory = (SampleTemplate?) GWT.create( SampleTemplate?.class ); // execute the template. Html tableWithRows = factory.createTableWithRows( rowData );
What do you think about HTML templating like here: http://www.gwtapp.org/