Developing with Sencha Ext GWT - Don't do it!

Impedence Mismatch for Client Side Views

The view technology for the web is to use HTML for the view, CSS for the styling of the view, and Javascript/JSON for client side functionality, objects and data.This stuff is easy and even 14 year olds at school can create resonable web pages using this technology.

Ext GWT uses Java classes, Layout Managers, Layout Containers and Hashmaps. Ah? you may ask. Well, you're damb right. If you've ever used Java Swing you'll know what I'm talking about. There is a reason why Java Swing never took off for creating web application. And that is because it is far eaier to create great looking web applications using Html, CSS and Javascript, than it is using LayoutManagers, RPC, and all that sort of crap.

Devloping sites using HTML, CSS, and Javascript frameworks is easy peasy.

However if you're using Ext GWT and you want do do anyting funky then it is almost impossible.

Also because of the structure of using Java code on the client and the server, you have to make sure that your Java classes can be serialiszed over the wire to the generated cleint side javascript application. There are just loads of classes which are 'black-listed' because they GWT compiler will not compile them to javascript (e.g. java.util.Calendar).

This means you have to convert any entity classes to some intermediatry class then can then be used in the serialization RPC mechansim. So on the server side you're working with say JPA entity objects, then you have to convert these to gxt ModelData objects, then send these over the wire, and convert them back to Enity objects when they come back to the server.

In otherwords what is generally a simple job, becomes time consuming, error prone, and just plain crap.

Conversion from ModelData (Hashmap) to Server-side Entities to Database Entities

This is one of the biggest pain-in-the-arse areas that gxt has. ModelData is just a Hashmap where you set values against keys. The reason why you have to use ModelData is because the RPC mechanism probably wont be able to deal with your normal domain object, particulary if they use runtime annotations such as JPA does. And this is because Ext GWT does not know how to serialise almost anything that is useful, because the serialization mechanism converts  Java source into Javascript, which means that it can not convert any client side specific stuff (such as persistance annotations).

So rather then use the world's standard client side model framework (known and JSON), Gxt/GWT have decided in their wizdom to create a whole new data architecture based on Hashmaps. So for every object you want to send over the wire you have to convert it into a wire friendly format (but not JSON because your Sencha classes do not work with JSON).

So the real big pain in the arse about all this is that if you get quite a complicated object graph from the database that you want to pass streight back to the client, you can't! You've got to write a load of code to convert it into 'view' objects. And that my friends, take a lot of time.

How crap is that, you may ask. Totally says I.

Also keep in mind that because some classes will not automatically be converted to the Ext-GWT RPC friendly format you'll have lots of fun spending many hours working out why you're getting serialization excpetions, and why your code does not work.

In fact, you should probably add 45% more time to your estimates if you working with Ext-GWT. Don't believe me? Then try this test:

Starting from a blank install of Eclipse the task is to create a "hello world" application. First page presents the user with a text field and a button. When the user clicks the button the contents of the text box is sent to the server via AJAX and on callback a dialog box pops up saying "Hello ContentsOfTextBox". First try that using normal HTML, Javascript (maybe JQuery), and server side Java. Then try it using Gxt.

I'll put money on it that the JQuery way of doing this will be completed before the Gxt framework is even installed.

If you want another test (because you're so dumb you think that once the setup cost have been concidered then gxt will be much more efficent), then try this test:

Get your best developer to create a Ext-GWT effect that looks like any of these JQuery effects. Then take a look at how long it took them to do it, and how much code they had to write. Personally, I would be supprised if they could do it at all. 

Setting the Width of Form Fields

Setting different width for form fields is fairly easy in HTML. The following form is...

First Name:
Age:

...is made up from the following styled HTML snippet. The important bits are hightlighted in bold.

<div style="border: solid 1px black;
           width:300px;
           padding:5px;
           font:15px tahoma,arial,helvetica,sans-serif;">

    <div style="padding-top:5px;">
       <span style="width:100px;float:left;">
            First Name:
      </span>
      <input type="text"/>
    </div>

    <div style="padding-top:5px;">
      <span style="width:100px;float:left;">
            Age:
      </span>
      <input type="text" size="5"/>
    </div>
</div>

In GXT, things are a little bit more complicated. To get the same effect you'll have to use the FormData object when adding the field to the FormPanel:

private LayoutContainer getNameAndAgeForm()
{
  final TextField<String> firstName = new TextField<String>();
  final TextField<Number> age = new TextField<Number>();
 
  firstName.setFieldLabel("First Name");
  age.setFieldLabel("Age");
 
  final FormPanel formPanel = new FormPanel();
  formPanel.setFrame(false);
  formPanel.setHeaderVisible(false);
  formPanel.setBodyBorder(false);
  formPanel.setLabelWidth(100);
 
  formPanel.add(firstName);
  formPanel.add(age, new FormData(100,-1));
 
  return formPanel;
}

Note the size in the <input> tag relates to the width in number of charactors, whereas in GXT the width relates to pixcels.

Button Colors

You might think that that the following GXT code would set the color of the buttong to yellow:

Button btn = new Button("Hello World");
btn.setStyleAttribute("backgroundColor", "yellow");

And would produce HTML like this:

<button style="background-color:yellow">Hello World</button>

But you would be wrong. It results in no differance to the button color. The HTML produced is:

<table cellspacing="0"
           role="presentation" id="x-auto-250"
           class=" x-btn x-component x-btn-noicon x-unselectable "
           style="background-color: yellow;" unselectable="on">
   <tbody class="x-btn-small x-btn-icon-small-left">
       <tr>
            <td class="x-btn-tl"><i>&nbsp;</i></td>
            <td class="x-btn-tc"></td>
            <td class="x-btn-tr"><i>&nbsp;</i></td>
      </tr>
      <tr>
         <td class="x-btn-ml"><i>&nbsp;</i></td>
         <td class="x-btn-mc">
            <em unselectable="on" class="">
              <button style="position: relative; width: 60px;" type="button" class="x-btn-text " tabindex="0">Hello World</button>
            </em>
         </td>
         <td class="x-btn-mr"><i>&nbsp;</i></td>
     </tr>
     <tr>
          <td class="x-btn-bl"><i>&nbsp;</i></td>
          <td class="x-btn-bc"></td>
          <td class="x-btn-br"><i>&nbsp;</i></td>
      </tr>
   </tbody>
</table>

In other words, you've just styled a <table> element, and your code has no effect on the Button.  You can't change the color of a Button in GXT.

Form Layouts

Here is a common and simple form layout (shows in Firefox - not bothered to optimise for IE, Chrome, etc. But I hope you get the point). Updating the HTML and CSS for working with all browsers will probably take about another hour.

Field 1:
Field 2:
Field 3:
Field 4:
Another:

Time taken using HTML and CSS: 20 minutes. . Here is the HTML:

 <div style="height: 160px; width: 580px;">
    <div style="height: 65px; width: 565px;">

        <div class="formBox">
            <div class="pad5">
                <div class="label">Field 1:</div>
                <input type="text" />
           
</div>
            <div class="pad5">
                <div class="label">Field 2:</div>
                <input type="text" />
           
</div>
        </div>

        <div class="formBox" style="float: right;">
            <div class="pad5">
                <div class="label">Field 3:</div>
                <input type="text" />
           
</div>
            <div class="pad5">
                <div class="label">Field 4:</div>
                <input type="text" />
            </
div>
        </div>

    </div>


    <div class="formBox" style="width: 95%;">
        <div class="pad5">
            <div class="label" style="vertical-align: top;">Another:</div>
            <textarea cols="41"></textarea>
       
</div>

    </div>
</div>

Here is the CSS:

<style>
input
{
  border:solid 1px #99BBE8;
}

div.formBox
{
  float:left;
  widht:10em;
  margin:5px;
  border:solid 1px #99BBE8;
  background-color:#DFE8F6;
}

div.label
{
  font: 12px tahoma,arial,helvetica,sans-serif;
  width:100px;
  display:inline-block;
}

div.pad5
{
  padding:5px;
}
</style>

Here is the code to produce the same form layout in GXT:

final TextField<String> field1 = new TextField<String>();
field1.setFieldLabel("Field 1");

final TextField<String> field2 = new TextField<String>();
field2.setFieldLabel("Field 2");

final TextField<String> field3 = new TextField<String>();
field3.setFieldLabel("Field 3");

final TextField<String> field4 = new TextField<String>();
field4.setFieldLabel("Field 4");

final TextArea textArea = new TextArea();
textArea.setFieldLabel("Another");

final FormPanel leftPanel = new FormPanel();
leftPanel.setStyleAttribute("margin", "5px");
leftPanel.setHeaderVisible(false);
leftPanel.add(field1);
leftPanel.add(field2);

final FormPanel rightPanel = new FormPanel();
rightPanel.setHeaderVisible(false);
rightPanel.setStyleAttribute("margin", "5px");
rightPanel.add(field3);
rightPanel.add(field4);

final LayoutContainer textFieldColumnContainer = new LayoutContainer();
textFieldColumnContainer.setLayout(new ColumnLayout());

textFieldColumnContainer.add(leftPanel, new ColumnData(.5));
textFieldColumnContainer.add(rightPanel, new ColumnData(.5));

final FormPanel textAreaPanel = new FormPanel();
textAreaPanel.setStyleAttribute("margin", "5px");
textAreaPanel.setHeaderVisible(false);
textAreaPanel.add(textArea, new FormData(550,-1));

final LayoutContainer page = new LayoutContainer();
page.add(textFieldColumnContainer);
page.add(textAreaPanel);

add(page); // Add the content page to whatever your using...

Time taken to do this? 30 minutes. So time is not really an issue. You also still have the issues of it looking OK in some browsers but not in others. OK in Firefox, not so good in Chrome.

For me the benifit of not using GXT is that you can optimise for different browsers much easier than if you use GXT. For example you can change the sizing CSS values to 'em's or percentages. With GXT your stuck with Sencha's implementation of the ridged use of either pixel width or percentages.

The other great thing about not using GXT is that anybody can understand the HTML version (i.e. millions of people), while only someone familiar with GXT will understand the GXT version (i.e. a few thousand people).

The other advantage is that the HTML version needs no special setup while the GXT version needs a development environment, compiler and applicaiton server. With the HTML version you can just cut and past the code into an HTML page and view it streight away.

GXT Radio Buttons

So how do you get the value from a GXT radio button. In normal HTML you may have a collection of radio buttons such as:

<input type="radio" name="color" value="RED" />Red</br>
<input type="radio" name="color" value="YELLOW" />Yellow</br>
<input type="radio" name="color" value="GREEN" />Green</br>

Then you can use a bit of javascript to show the value of the clicked button:

<script type = "text/javascript">
 $(document).ready(function()
 {
    $('input[name="color"]').click(function(event)
    {
      var valueOfClickedButton = event.target.value;
      alert(valueOfClickedButton);
    });
 });

</script>

Easy.

How do you do this in GXT? The problem is with GXT's Radio class. You would expect the getValue() method to return the value set (i.e. RED, YELLOW or GREEN from the HTML example above). But the getValue() and setValue() methods are booleans and used to set whether the Radio is checked or not.

You may think that using the getName()method could be used. But you would be wrong - this method will return something like "gxt.RadioGroup.n" no matter what you did in setName(String). So you how about using getFieldLabel()? Well, again, no matter what you did in setFieldLabel(String) it will always return an empty String. 

To get the value of a clicked radio button in GXT you must use the setValueAttribute(String) method. May be obvious once you know, but getting to know is the difficult bit. Check out the following code snippet:

 

final Radio red = new Radio();
red.setBoxLabel("Red");
red.setValueAttribute("RED");

final Radio yellow = new Radio();
yellow.setBoxLabel("Yellow");
yellow.setValueAttribute("YELLOW");

final Radio green = new Radio();
green.setBoxLabel("Green");
green.setValueAttribute("GREEN");

final RadioGroup colorsGroup = new RadioGroup();
colorsGroup.setFieldLabel("Colors");
colorsGroup.add(red);
colorsGroup.add(yellow);
colorsGroup.add(green);

colorsGroup.addListener(Events.Change, new Listener<BaseEvent>() {
  @Override
  public void handleEvent(BaseEvent be)
  {
    final RadioGroup radioGroup = (RadioGroup)be.getSource();
    
    final Radio clickedRadioBtn = radioGroup.getValue();

    // 'name' will equal "gxt.RadioGroup.n"
    final String name = clickedRadioBtn.getName();

    // 'fieldLabel' will equal "" - empty string
    final String fieldLabel = clickedRadioBtn.getFieldLabel();

    // 'value' will always be true
    final boolean value = clickedRadioBtn.getValue();

    // Correct !!! 'valueAttribute' will equal the color set via setValueAttribute(String)
    final String valueAttribute = clickedRadioBtn.getValueAttribute();
  }
});

 

Inconsistances - FormBinding

If you have a form full of lots of fields, you can use a FormBinding object to bind a ModelData object to those fields. So long as the names of the fields and the property keys of the model data match then the binding will be automatic and data will be transfered from the field to the model and visa versa. Neat. 

But FormBinding only works with a FormPanel. If you're using a FieldSet, then you're back to the long handed method of reading and writting values.

 

  1.  FormBinding binding = new FormBinding(panel);  
  2.  binding.autoBind();  
  3.  binding.bind(stock);

 

Here is a short list of sites that could not be developed using Ext-GWT:

  1. Twitter
  2. Facebook
  3. BBC
  4. <Any other cool looking website>

But if you still wanting to use Gxt then here is a list of places where it would be a good option:

  1. Boring back-office systems
  2. Sites for which you don't want users
  3. Consultancies looking to make loads-o-money from Change Requests

Difficult to run Cleint side Tests Against (e.g. Selinum)

Add comment