Archive for June, 2009

Tree Grid, Grouped Headers and Aggregation Rows – Ext GWT 2.0 M3 Released

Monday, June 15th, 2009

The Ext team is pleased to announce the availability of Ext GWT 2.0 M3. M3 will be the last milestone release. We will be releasing a release candidate before the final release of Ext GWT 2.0. Many thanks to the early adopters in the community that have been using 2.0 and reporting bugs.

Ext GWT 2.0 M3 contains several new features and components.

Grid Enhancements

We have added two new features we hope you will enjoy. First is column grouping, which allows multi-row headers with colspan and rowspan support. In addition, widgets can be added to the headers.

Column Grouping

The follow code creates the two rows of column groups:

    cm.addHeaderGroup(0, 0, new HeaderGroupConfig("Header Grouping Example", 1, 5));
    cm.addHeaderGroup(1, 2, new HeaderGroupConfig("Stock Performance", 1, 2)); 
    cm.addHeaderGroup(1, 0, new HeaderGroupConfig(yourWidget, 1, 2));

The second enhancement is aggregation rows. One to many rows can be added to the bottom of a Grid. You can specify one of the predefined aggregations types, such as max, min, and avg, or use renderers to display any data.

Aggregation Rows

The following code creates a single aggregation row:

AggregationRowConfig<Stock> averages = new AggregationRowConfig<Stock>();
averages.setHtml("name", "Average");
 
// with summary type and format
averages.setSummaryType("last", SummaryType.AVG);
averages.setSummaryFormat("last", NumberFormat.getCurrencyFormat());
 
// with renderer
averages.setSummaryType("change", SummaryType.AVG);
averages.setRenderer("change", new AggregationRenderer<Stock>() {
  public Object render(Number value, int colIndex, Grid<Stock> grid, ListStore<Stock> store) {
    // you can return html here
    return number.format(value.doubleValue());
  }
});
cm.addAggregationRow(averages);

Tree Panel & Tree Grid

With the M3 release there are some exciting changes.

M3 introduces the TreePanel component. TreePanel binds directly to a TreeStore and replaces Tree, TreeItem, TreeBinder. Even more exciting, the new TreeGrid replaces TreeTable, TreeTableItem, and TreeTableBinder.

This new design gives you all the benefits and features of Grid – fast rendering, widget support, and inline editing – with the additional features of widget support previously found in Table. Grid now supports placing widgets in cells using a GridCellRenderer.

The following screenshot is of the new TreeGrid using a RowEditor:

TreeGrid

ImageBundle Support

ImageBundle support has been an often requested feature. With M3, we have added ImageBundle support. Icons can be specified in three different methods:

  • AbstractImagePrototype (typically from an ImageBundle)
  • CSS style name (existing method)
  • Image path (String)

All components that support icons now implement the new IconSupport interface:

/**
 * Interface for objects that support icons.
 */
public interface IconSupport {
  /**
   * Returns the icon.
   * 
   * @return the icon
   */
  public AbstractImagePrototype getIcon();
 
  /**
   * Sets the icon.
   * 
   * @param icon the icon
   */
  public void setIcon(AbstractImagePrototype icon);
 
  /**
   * Sets the icon style.
   * 
   * @param icon a CSS style name
   */
  public void setIconStyle(String icon);
}

In addition, there is a helper class, IconHelper, that can be used to create image prototypes from CSS style names, and image paths. Here is an example setting an icon 3 different ways:

    // from bundle
    item.setIcon(GXT.IMAGES.editor_bold());
    // CSS style name
    item.setIconStyle("my-icon");
    // image path
    item.setIcon(IconHelper.createPath("/my/url/foo.gif"));

When using ImageBundles, you create classes that extends ImageBundle. Here is a partial look of the new XImages class, which is the ImageBundle GXT uses for all it’s icons:

public interface XImages extends ImageBundle {
  @Resource("hmenu-asc.gif")
  AbstractImagePrototype grid_sortAsc();
 
  @Resource("hmenu-desc.gif")
  AbstractImagePrototype grid_sortDesc();
  ....
}

One immediate benefit we have noticed, is that the icons display immediately when first displayed in the application, rather than incrementally as a page loads. This happens since all the images are combined into one on the server at compile time, and therfore, 1 http request. While we are talking about it, here is a sreenshot of the image GWT generates from the individual icons we specified in our XImage class:

Binders Deprecated

Ext GWT 1.0 included a set of “binders” classes that added store and model support to widgets. With the binders, you worked with stores and models, rather than widgets directly such as TreeItem’s and TableItem’s. With binders, there is the wrapped widget, the wrapped widget’s child widget, and the binder itself:

  • Tree & TreeItem with TreeBinder
  • TreeTable & TreeTableItem with TreeTableBinder
  • Table & TableItem with TableBinder
  • DataList & DataListItem with DataListBinder

With 2.0, the “binders” and their wrapped widgets have been deprecated. Don’t confuse “binders” with our data binding code, which is something different. As mentioned above, Tree, TreeItem, TreeBinder TreeTable, TreeTableItem, and TreeTableBinder have been deprecated. In addition, DataList, DataListItem, and DataListBinder have been deprecated in favor of the existing ListView component which binds directly to a ListStore. With these changes, all of our data bound components have the same design, using stores and models. There are equivalents to every function and feature the deprecated functions provided, so there’s a smooth upgrade path.

Upgrading

Ext GWT 2.0 is a major upgrade from 1.0. There are a number of breaking changes to consider when moving from 1.X. There is a migration guide in the Ext GWT download which documents the API changes. Make sure to take a look at this before you begin upgrading to 2.0.

We are offering a pre-release sale with hefty discounts to upgrade your 1.x license. If you’ve thought about purchasing an Ext License, for a limited time, you can purchase online for less than an Ext GWT 1.x License. There’s no better time to support the Ext team.

Building a Rating Widget with Ext Core 3.0 Final and Google CDN

Wednesday, June 10th, 2009

We are very proud to announce the final release of Ext Core under the MIT license. Your feedback was invaluable. Thank you for all the bugs reported and test cases created. For those of you who are new to Ext Core, we suggest you read the previous blog post about the all the features and examples that we released as part of the beta. You can find a list of changes and fixes we made for the final here.

For this post we will leverage the power of Ext by creating and dissecting a useful star rating example. We hope to share some of the general best practices behind creating unobtrusive, reusable code with Ext Core to liven up your pages.

Making a Splash

Including Ext Core on your site is easier than ever. We are honored to share with the community that Ext Core is now available via the Google AJAX Library API. Many thanks to Ben Lisbakken at Google for working with us to make this a reality.
//any of these will work ;)  <script type="text/javascript" src="....
http://ajax.googleapis.com/ajax/libs/ext-core/3.0/ext-core.js
http://ajax.googleapis.com/ajax/libs/ext-core/3/ext-core.js
http://ajax.googleapis.com/ajax/libs/ext-core/3.0/ext-core-debug.js
http://ajax.googleapis.com/ajax/libs/ext-core/3/ext-core-debug.js
Alternatively, you can use Google AJAX API Loader's google.load() method:
google.load('ext-core', '3');
google.load('ext-core', '3', {uncompressed : true});

Getting Started

Ext Core is a perfect fit for adding behavior to existing HTML. When designing a widget, having a markup structure that provides graceful degradation is an added plus. For this example, we will be using radio buttons. We can "group" the elements to specify which radio buttons are part of the control. It could look something like the following:
<div id="rating1">
    <input type="radio" name="rating1" value="1" title="Very poor">
    <input type="radio" name="rating1" value="2" title="Not that bad">
    <input type="radio" name="rating1" value="3" title="Average">
    <input type="radio" name="rating1" value="4" title="Good">
    <input type="radio" name="rating1" value="5" title="Perfect">
</div>
This markup allows user to have the ability to rate an item even without the fancy stars and additional functionality that we have in mind. Take notice that this simple markup contains powerful information like the name, value and title for each item which we can reuse with our rating widget.

The API

One of the most important aspects of building reusable code is providing your developers a powerful API. Our aim here is to allow developers to progressively enhance and convert the markup into a star rating with a simple API. In this case we will need the element that wraps around the radio controls, and some optional configuration to customize the behavior of the widget. Following the Ext tradition we will provide these configuration options in the form of an object literal. A possible API for our widget could look like this:
//Keep it simple
new Ext.ux.Rating('rating1', {
    showTitles: true
});

Ext.util.Observable

So now that we know how we want to use our component, lets go ahead and actually look at some details on how to write it! In our previous post we mentioned that Ext Core allows you to write neatly structured object-oriented code. Whenever you want to create a piece of functionality, you should try to bundle it into a separate class. In most cases you will need to be able to listen for events on instances of your class. Ext provides a power class, the Ext.util.Observable class, to springboard your development. This is the same class that almost all classes in Ext JS extend from! Our basic shell for our rating plugin could look something like this:
Ext.ns('Ext.ux');
Ext.ux.Rating = Ext.extend(Ext.util.Observable, {
    // Configuration default options
    showTitles: true,
 
    // Our class constructor
    constructor : function(element, config) {
        Ext.apply(this, config);      
        Ext.ux.Rating.superclass.constructor.call(this);     
 
        this.addEvents( 'change');  
 
        this.el = Ext.get(element);
        this.init()
    }
});
For those of you familiar with Ext JS will recognize this pattern. In this piece of code we have created a namespace to put our class into using the Ext.ns() function. Then we create our class using the Ext.extend method with Ext.util.Observable as the base class. We define some configuration options with default values and then set up our constructor method which will be called when we create a new instance of our class. We also define some custom events and wrap the element passed to the constructor with an Ext.Element instance. We use the Ext.get() flexible nature to have support of passing an id string, DOM Element or Ext.Element to our constructor.

Reaching the stars

It is time to think about the things we need to get our widget working. First we want to replace the radio buttons with our stars, we will need to store the values and titles for each star, we want to create a hidden input to put the current value in and finally we need to set up event listeners to listen for mouse hovers and clicks.
init : function() {
    var me = this; 
 
    // Some arrays we are going to store data in
    this.values = [];
    this.titles = [];
    this.stars = [];
 
    // We create a container to put all our stars into
    this.container = this.el.createChild({
        cls: 'ux-rating-container ux-rating-clearfix'
    });
 
    // We use DomQuery to select the radio buttons
    // Then we can loop over the CompositeElement using each
    this.radioBoxes = this.el.select('input[type=radio]');
    this.radioBoxes.each(this.initStar, this);
 
    // We use DomHelper to create our hidden input
    this.input = this.el.createChild({
        tag: 'input',
        type: 'hidden',
        name: this.name,
        value: this.values[this.defaultSelected]
    });
 
    // Lets remove all the radio buttons from the DOM
    this.radioBoxes.remove();
 
    if(this.disabled) {
        this.disable();
    } else {
        // Enable will set up our event listeners
        this.enable();
    }
}

Creating Stars - using DomHelper and accessing the DOM from Ext Element

 
initStar : function(item, all, i) {
    // We use the name and disabled attributes of the first radio button 
    if(i == 0) {
        this.name = item.dom.name;
        this.disabled = item.dom.disabled;		
    }
 
    // Saving the value and title for this star     
    this.values[i] = item.dom.value;
    this.titles[i] = item.dom.title;
 
    // Now actually create the star!
    var star = this.container.createChild({
        cls: 'ux-rating-star'
    });
 
    // Save the reference to this star so we can easily access it later
    this.stars.push(star.dom);
},

Enable and Select Stars - listening for events, using the target of an event and firing custom events

 
enable : function() {
    // ... some code missing here ...
 
    // We will be using the technique of event delegation by listening
    // for bubbled up events on the container       
    this.container.on({
        click: this.onStarClick, 
        mouseover: this.onStarOver,
        mouseout: this.onStarOut,
        scope: this,
        delegate: 'div.ux-rating-star'
    });        
},
 
onStarClick : function(ev, t) {
    if(!this.disabled) {
        this.select(this.stars.indexOf(t));
    }
},
 
select : function(index) {
    // ... some code missing here ...
    else if(index !== this.selected) {
        // Update some properties           
        this.selected = index;
        this.value = this.values[index];
        this.title = this.titles[index];
 
        // Set the value of our hidden input so the rating can be submitted			
        this.input.dom.value = this.value;
 
        // the fillTo() method will fill the stars up until the selected one
        this.fillTo(index, false);
 
        // Lets also not forget to fire our custom event!         
        this.fireEvent('change', this, this.values[index], this.stars[index]);  
}

Filler Up - dom manipulation (adding classes)

 
fillTo : function(index) {
    var cls = 'ux-rating-star-on';
 
    // We add a css class to each star up until the selected one   
    Ext.each(this.stars.slice(0, index+1), function() {
        Ext.fly(this).addClass(cls);
    });
 
    // And then remove the same class from all the stars after this one
    Ext.each(this.stars.slice(index), function() {
        Ext.fly(this).removeClass(cls);
    });      
}

We won't discuss all the details since most of it is pretty straightforward, but the final product should give you a general idea of how to use the basic functionality available in Ext Core to tie together all the missing pieces.

Wrapping it up

In this example we used the following cross-browser compatible functionality available in Ext core:
  • Classical Inheritance Class System
  • Observable Class
  • DomQuery
  • DOM manipulation and traversal
  • Event handling
  • Markup generation

Ext Core makes it fun to write code, and helps you create clean, well-structured classes using a set of cross-browser abstractions on the existing browser API's. For those of you who want to use it or are just interested in seeing the completed work, we have included a version of the widget in the Ext Core Final build. You can see the working widget embedded in the post below:

The example page illustrating this widget can be found here.

Final words

We hope that this library will find its way into many of your dynamic web pages and make your lives as web developers easier and more enjoyable. We are looking forward to seeing the great things you will create using it. We are always looking for ways to make this library better and we think the best way to do that is by listening to your suggestions. So, don't be shy and tell us what you think.

Ext JS 3.0 RC2 Release – Stable, Robust, and Enhanced

Wednesday, June 3rd, 2009

We are pleased to announce that the latest release candidate of Ext 3.0 is now publicly available. We are very proud of the stability of this release. We’d like to thank our support team and elite community members who have tested the release candidates. You have assisted in squashing a number of bugs affecting both Ext Core and Ext JS. The time taken to report issues and create test cases is much appreciated.  The list of issues resolved for this deployment can be found for Ext Core and Ext JS separately. Some of the major fixes include:

  • Items are now automatically laid out when they are first shown – rather than trying to calculate dimensions when they are hidden. This will solve a number of layout issues that occur across all components.
  • The toolbar overflow has been improved to support all toolbar items, including CycleButtons and Buttons with toggle enabled (both grouping and otherwise).
  • Issues with some animations in the Fx library have been corrected.

New Examples

Several new examples have been added in this version to help you get up and running with Ext 3.0 quickly.

  • A new example detailing the new REST support for stores has been included, which supports full CRUD operations.
  • // A single store using the Proxy, Reader and Writer together
    // through a RESTful interface
    var store = new Ext.data.Store({
           id: 'user',
           restful: true, // <-- This Store is RESTful
           proxy: proxy,
           reader: reader,
           writer: writer // <-- plug a DataWriter into the store just as you would a Reader
    });
  • An excellent demo illustrating the use of multiple providers together is now available. Here we use the Ext.direct polling to make regular requests to get the server’s time. These requests can be interspersed with those of a remoting provider, which allows both an echo and a multiplication to take place.
  • <script type="text/javascript" src="php/api.php"></script> //Ext.app.REMOTING_API
     
    <script type="text/javascript">
    Ext.onReady(function(){
        Ext.Direct.addProvider(
                Ext.app.REMOTING_API,
               {
                   type:'polling',
                   url: 'php/poll.php'     //additional Provider
               }
         );
    });
    </script>
  • An additional example demonstrates the new writer capabilities of Ext. It provides full CRUD support using a grid, without any use of Ext direct. It shows how the writer can be used with legacy style server interaction.

Writer Sample
Writer Sample

New Features

It wouldn’t be a cool release if we didn’t add some goodies. The documentation has been significantly improved, with a number of classes getting extra information or examples added to their reference page. Peruse the new and updated documentation to find some hidden gems.

REST support

Many web frameworks today are implementing REST services to simplify the CRUD process. With Ext’s new data package enhancements, designing a RESTful Store is a snap. Simply plug a suitable DataWriter extension, like JsonWriter, into any Store along with a standard HttpProxy and set the new Store configuration-property restful: true. Your Store will now automatically generate GET, POST, PUT and DELETE requests to your server.

DataWriter

The Ext JS 3.0 data package introduces compelling enhancements with a new component called DataWriter (along with descendant JsonWriter). These collection of enhancements will simplify your interaction with Ext.data.Store by automatically generating CRUD requests to your server-side framework. Once you plug a suitable DataWriter extension, like JsonWriter, into your Store you’ll never have to manually compose Ajax CRUD requests to your server again — it’s all automated now and highly configurable.

//define and we'll handle the rest
var proxy = new Ext.data.HttpProxy({
    api: {
        read    : 'app.php/users/read',
        create  : 'app.php/users/create',
        update  : 'app.php/users/update',
        destroy : 'app.php/users/destroy'
    }
});

Ext.Error

A base error class has been added, with the intention of extending this to provide more robust error handling throughout the framework in the debug build. As part of this, we will be looking at introducing extra code into the debug build to check for common errors and problems. The developer will be alerted to the issue allowing them to quickly find the point of failure and rectify the problem. These extra checks will be automatically removed in the production build so that performance is not negatively impacted. Stay tuned for more details.

As an example, here are some errors used in data namespace to make it easier for new and seasoned developers:

"DataProxy attempted to execute an API-action but found an undefined url / function.  
Please review your Proxy url/api-configuration."

"Could not locate your "root" property in your server response.  
Please review your JsonReader config to ensure the config-property "root" matches the property 
your server-response.  See the JsonReader docs for additional assistance."

We look forward to community input on where we should add additional checks based on your experiences.

Ext.direct interoperation with Ext.Component subclasses

The main feature that has been added in this release candidate is extra interoperability between Ext.direct and other Ext.Component classes. Both the Ext.tree.TreePanel and Ext.form.FormPanel can now load their data via Ext.direct.

Loading a tree using Ext Direct

In this case our server side method takes a single parameter (the id of the node) and returns an array of JSON nodes. An example of this can be found here.

API Definition:

Ext.app.REMOTING_API = {
    "url": "php/router.php",
    "type": "remoting",
    "actions": {
        "TestAction": [{
            "name": "getTree",
            "len": 1
        }]
    }
};

Sample code

Ext.onReady(function(){
    Ext.Direct.addProvider(Ext.app.REMOTING_API); //setup provider
 
    var tree = new Ext.tree.TreePanel({
        width: 400,
        height: 400,
        autoScroll: true,
        renderTo: document.body,
        root: {
            id: 'root',
            text: 'Root'
        },
        loader: new Ext.tree.TreeLoader({
            directFn: TestAction.getTree //specify directFn on tree
        }),
        fbar: [{
            text: 'Reload root',
            handler: function(){
                tree.getRootNode().reload();
            }
        }]
    });
});

Forms and Ext Direct

By popular demand, we’ve added Ext.direct for loading and submitting data via forms.

var form = new Ext.form.FormPanel({
               api: {
                      load: App.ss.ClientForm.load,   
                      submit: App.ss.ClientForm.submit
                      },
                paramOrder: ['uid'],
                defaultType: 'textfield',
                items: [/* Ext.form.Fields go here */]
});

We encourage you to download and use the latest release candidate. We hope you enjoy using Ext JS 3.0 – we had a blast creating it.

Lastly, following a tradition started with Ext 1.0, we are offering a pre-release sale with hefty discounts to upgrade your 2.x license. If you’ve thought about purchasing an Ext License, for a limited time, you can purchase online for less than an Ext 2.x License. There’s no better time to support the Ext team. Enjoy.