Archive for the ‘Ext JS’ Category

Ext JS Designer Preview

Thursday, October 8th, 2009

We are very excited to share our latest version of the Ext JS Designer. This new version adds many new features to improve your efficiency creating application designs. Once you get accustomed to these features its difficult to live without them. For those of you that don't have the time or ability to download and play around with the Designer, we have created a Preview screencast in which we mock up some interfaces. We have tried to show off as much features and functionality as possible.

New Features

  • Duplicating Components
  • Transforming Components
  • Undo/Redo
  • Configuration Searching
  • Auto Updating
  • Screenshots

Duplicating Components

How many times have you copied and pasted a set of source code and/or configuration and modify a couple of values to save on some keystrokes? The designer now has the same ability by providing you the ability to duplicate sets of components and then modify their values.

Take a look at this quick sample where I've leveraged the duplicate functionality to quickly build a form without dragging and dropping the components and setting up common configurations over and over.

Building a simple form:
Building a simple form

Transforming Components

When developing you are capable of quickly changing a component from one class to another without losing your configurations by changing the class that you are extending from or instantiating.

For Example:
MyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
 
});
// or
new Ext.grid.EditorGridPanel({
 
});

Developing in the designer should not be any different. If after creating a GridPanel I decide that I really meant to use an EditorGridPanel. I can right click on the component within the inspector and see which transformation options are available.

A GridPanel can be transformed to an EditorGridPanel and vice-versa.
A GridPanel can be transformed to an EditorGridPanel and vice-versa

Let's put together a GridPanel. When we drag out a field for a particular column the designer intelligently knows that in order to use a TextField within a column it must convert itself to an EditorGrid. The designer will automatically perform this transformation for you.

Loan Application Grid

Another interesting transformation is converting a TabPanel to a Panel and then setting the layout to accordion. Fields are capable of transforming between types.

Component Undo/Redo

If you make a mistake while putting together your application you can undo/redo the last change that was made via the Undo and Redo buttons in the top toolbar. A current limitation of this feature is that if you perform a transform on a top level component then the history is reset.

Configuration Searching

Searching for 'la' in a Ext.form.FormPanel.A critical feature missing in prior releases was the ability to search configurations which are available to the component you have currently selected. In the past, if you were searching for a component configuration you had to look through the entire list available! The screenshot to the right demonstrates searching for the 'la' configuration in a FormPanel.

You can now unset configurations by clicking on the x on the right hand side of the configuration. This is useful when you want to remove setting a configuration and not just set it to "".

Newly Added Components

Some of the exciting new components added are:
  • EditorGrid
  • ButtonGroup
  • BoxComponent
  • Slider

Auto-Update

We are using Adobe AIR's Auto Update framework to provide updates to the designer as we push out new public releases. When we release a new version you will be prompted to update to the latest version. You can expand out the "Release Notes" to see what enhancements and bug fixes have been made. Adobe AIR provides us the ability to quickly deploy updates to all of you as new versions are released seamlessly.

Auto Updating the Designer via Adobe AIR
Auto Updating the Designer via Adobe AIRScreenshots - Leveraging ActionScript within Ext JS

One feature we wanted to implement within the designer was taking screenshots of your budding prototype directly within the application. Imagine this, you quickly mock up a borderlayout complete with tabs, a grid, and a form and then click the screenshot button. You can then choose to save the screenshot of the component you've just created as a PNG. We thought that this would probably be a simple task within AIR but ended up having to do quite some digging to figure out how to accomplish it.

In order to create a screenshot of the applications current state we can use air.BitmapData to retrieve the raw bitmap data of the current screen by calling the draw method.

var capture = new air.BitmapData( window.htmlLoader.stage.stageWidth, 
                                  window.htmlLoader.stage.stageHeight);
capture.draw( window.htmlLoader );

The contents of the variable capture now has the raw bitmap data of our entire application. We'd like to convert this into a modern format like PNG and save it to the file system. We will use PNGEncoder from as3corelib. You can compile and use any arbitrary actionscript code to a SWF and expose it to JavaScript within Adobe AIR by including it in the page by giving it a type of application/x-shockwave-flash.

Use the mxmlc compiler from the Flex SDK to compile the code:
aaron@aaron-desktop:~/as3corelib-.92.1/src$ mxmlc -source-path=. com/adobe/images/PNGEncoder.as 
Loading configuration file /home/aaron/FlexSDK/frameworks/flex-config.xml
/home/aaron/as3corelib-.92.1/src/com/adobe/images/PNGEncoder.swf (1242 bytes)
You can then copy the file into your project and include the file:
<script src="adobe/PNGEncoder.swf" type="application/x-shockwave-flash"></script>
We can now encode the raw bitmap data and write it to the file system:
var file = air.File.documentsDirectory.resolvePath('screenshot.png');
var stream = new air.FileStream();
// Encode image captured from air.BitmapData
var png = window.runtime.com.adobe.images.PNGEncoder.encode( capture );
 
stream.open( file, air.FileMode.WRITE );
stream.writeBytes( png, 0, 0 );
stream.close();

When you import classes from ActionScript into your HTML App they will immediately be placed in the window.runtime namespace with their appropriate package namespaces.

Therefore our class com.adobe.images.PNGEncoder is placed in window.runtime.com.adobe.images.PNGEncoder. (As an aside this is all AIRAliases.js file which you include simply provides smaller aliases from window.runtime into the air namespace.)

When taking the screenshot we also use a clipping rectangle and the copyPixels method to to grab only the bounding rectangle of the currently active component.

Summary

We've added lots of features which should improve your productivity when building user interfaces with the designer. We hope you like our progress and as always we greatly appreciate your feedback. For those of you that have suggestions, questions, found bugs, or just want to make a remark, we are listening.

Ext JS on Rails: A ComprehensiveTutorial

Wednesday, September 30th, 2009

I’ve had my eyes on Ruby-based ExtJS code-generation tools for a few years now. Back in Ext-1.0 days, I even took a shot at creating a large Rails wrapper framework, mapping Ext UI widgets such as Windows, Grids, Trees, Forms and so on, to plain-old Ruby-objects which could be stored in YAML files and rendered into views. However, with Ext-2.0 came new ideas which brought many changes to the framework (great new component-model, plugins, xtype, normalized component configuration-objects) and the Rails wrapper framework was rendered immediately obsolete. Until recently, I gave up on auto-generating ExtJS code and concentrated upon writing good Ext plugins and base-classes.

New possibilities

One of the more tedious processes of creating Ext apps is rendering the DataReader, DataProxy and Store components without the luxury of javascript helpers:

//The traditional way
 
// JsonReader
var reader = new Ext.data.JsonReader({
    root: 'data',
    idProperty: 'id',
    successProperty: 'success'
}, [{
    name: 'id'
}, {
    name: 'email',
    allowBlank: false
}, {
    name: 'first',
    allowBlank: false
}, {
    name: 'last',
    allowBlank: false
}, {
    name: 'created_at',
    type: 'date',
    dateFormat: 'c'
}, {
    name: 'updated_at',
    type: 'date',
    dateFormat: 'c'
}]);
// JsonWriter
var writer = new Ext.data.JsonWriter({
    encode: false
});
// HttpProxy
var proxy = new Ext.data.HttpProxy({
    url: '/users.json'
});
// Typical Store
var store = new Ext.data.Store({
    name: 'users',
    id: 'user',
    restful: true,
    proxy: proxy,
    reader: reader,
    writer: writer,
    autoLoad: true,
    autoSave: true
});

Over the past few weeks, I dusted off my rusty ruby hat and took a fresh look at augmenting ActiveRecord, ActionController and ActionView to help us auto-render a few Ext JS components.

Ext MVC for Rails

I’ve published a new gem at Github named extjs-mvc, a rather simple gem consisting of three mixins for ActionController, ActiveRecord and a View Helper. However, the Gem is hosted at Gem Cutter, “the next generation of RubyGem hosts”. To install the extjs-mvc Gem, first install the gemcutter Gem.

$ sudo gem install gemcutter
$ gem tumble
Thanks for using Gemcutter!
Your gem sources are now:
- http://gemcutter.org
- http://gems.rubyforge.org/
- http://gems.github.com

The gem tumble line adds Gemcutter as your primary Gem-source. gem tumble operates as a toggle — executing it again will remove Gemcutter as a source. Gemcutter seems like a fantastic service — no more naming issues with Github-hosted, username-prefixed gems!

So with Gemcutter added as your primary Gem-source, go ahead and install the extjs-mvc Gem:

$ sudo gem install extjs-mvc

Using the extjs-mvc gem, the Store above can now be rendered in a more Rails/Merb-friendly manner using the extjs_store helper method inside an erb template:

<div id="grid"></div>
<script>
<%= extjs_store({
    :controller => "users",
    :format => "json",
    :config => {
        "autoLoad" => true,
        "restful" => true,
        "writer" => {
            "encode" => false
        }
    }
})%>
</script>

Which renders a tidy little JsonStore instance.

new Ext.data.JsonStore({
    "restful": true,
    "url": "/users.json", // url comes from inflecting upon the controller name + :format
    "fields": [{               //  Fields come from ActiveRecord::Base#columns
        "type": "int",
        "allowBlank": false,
        "name": "id"
    }, {
        "type": "string",
        "allowBlank": false,
        "name": "first"
    }, {
        "type": "string",
        "allowBlank": false,
        "name": "last"
    }, {
        "type": "string",
        "allowBlank": false,
        "name": "email"
    }, {
        "type": "date",
        "allowBlank": true,
        "name": "created_at",
        "dateFormat": "c"
    }, {
        "type": "date",
        "allowBlank": true,
        "name": "updated_at",
        "dateFormat": "c"
    }],
    "messageProperty": "message",
    "root": "data",
    "successProperty": "success",
    "idProperty": "id",     // ActiveRecord::Base#primary_key
    "storeId": "user",      // Inflecting upon controller name
    "autoLoad": true
});

I’ve created an in-depth tutorial on how to create an Ext JS application without compromising your Rails methodology.

rails

Step 1: A Fresh Start

Let’s create a new rails app and take it from the top (Note: using the extjs-mvc gem requires ext-3.0.1+):

$ cd workspace
$ rails extonrails
$ cd extonrails
$ ln -s /www/shared/js/ext-3.0.1 public/javascripts/ext-3.0.1
$ rm public/index.html

Next, configure the extjs-mvc gem in environment.rb within the Rails::Initializer.

Rails::Initializer.run do |config|
    config.gem "extjs-mvc"
end

Edit views/layouts/application.html.erb and link-up the Ext framework in the usual manner.

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    <meta http-equiv="imagetoolbar" content="no" />
    <meta name="MSSmartTagsPreventParsing" content="true" />
 
    <title>Ext on Rails</title>
 
    <%= stylesheet_link_tag '/javascripts/ext-3.0.1/resources/css/ext-all', 'app', 'silk' %>
    <%= javascript_include_tag 'ext-3.0.1/adapter/ext/ext-base', 'ext-3.0.1/ext-all' %>
</head>
 
<body>
    <div id="hd">
        <img src="/images/rails.png" />
        <h1>ExtJS on Rails</h1>
        <div class="x-clear">&nbsp;</div>
    </div>
    <div id="nav"></div>
    <div id="bd"><%= @content_for_layout %></div>
    <div id="ft"></div>
</body>
</html>

There are a few extra stylesheets included here, app.css and silk.css. Download them if you wish. If you include silk.css, you’ll have to download the silk icon pack from famfamfam. silk.css expects the icons to exist in a folder named /images/icons/silk.

Now let’s generate a controller named projects

$ script/generate controller projects

Create an erb template named app/views/projects/step1.html.erb to make sure we’re cooking.

<h1>Step1</h1>

Go ahead and start your server. You should see something like this

Step 2: Auto-generating Ext components in Rails

The extjs-mvc Gem includes a helper named ExtJS::Helpers::Component which can auto-generate Ext Component configurations compatible with Ext.ComponentMgr#create.

Add a new action to projects_controller.rb named step2. Include the helper ExtJS::Helpers::Component from the extjs-mvc Gem.

class ProjectsController < ApplicationController
  helper ExtJS::Helpers::Component
end

Create a new erb template projects/step2.html.erb.

<div id="panel"></div>
 
<%
    panel = extjs_component(
        "xtype" => 'panel',
        "iconCls" => "silk-application",
        "title" => 'Auto-generated Ext.Panel!',
        "renderTo" => 'panel',
        "frame" => true,
        "width" => 300,
        "height" => 100,
        "html" => 'Why are my feet so far from my head?',
        "collapsible" => true,
        "buttonAlign" => "center",
        "buttons" => [
            {"text" => "Save", "iconCls" => "silk-disk"},
            {"text" => "Cancel", "iconCls" => "silk-cancel"}
        ]
    )
 
    extjs_onready(panel)
%>
 
<%= extjs_render %>

View /projects/step2 in your browser. It should look something like Step2. Let’s step through it:

    panel = extjs_component(...)

The helper-method extjs_component included in the helper ExtJS::Helpers::Component accepts the same configuration options as any Ext Component. extjs_component actually returns an instance of a plain old ruby-object named ExtJS::Component. ExtJS::Component has just three public methods, add, to_json and render.

    extjs_onready(panel)

This line adds the Component instance to the on_ready queue provided by the helper ExtJS::Helpers::Component.

<%= extjs_render %>

extjs_render iterates the on_ready queue and converts each added component to json. You must always call this method in your view or nothing will render.

View the source of /projects/step2 and have a look at the result of extjs_render:

Ext.onReady(function() {
    Ext.ComponentMgr.create({
        "html": "Why are my feet so far from my head?",
        "buttons": [
            {
                "text": "Save",
                "iconCls": "silk-disk"
            },
            {
                "text": "Cancel",
                "iconCls": "silk-cancel"
            }
        ],
        "title": "Auto-generated Ext.Panel!",
        "items": [],
        "frame": true,
        "height": 100,
        "xtype": "panel",
        "buttonAlign": "center",
        "collapsible": true,
        "width": 300,
        "renderTo": "panel",
        "iconCls": "silk-application"
    });
});

Step 3: Adding Event Handling

Attaching event-listeners using extjs_component presents a bit of problem, since json-conversion forces each key/value pair to be surrounded in double-quotes. The problem was easily solved though. Have a look at the render method of ExtJS::Component.

def render
  # If there are any listeners attached in json, we have to get rid of double-quotes in order to expose
  # the javascript object.
  # eg:  "listeners":"SomeController.listeners.grid" -&gt; {"listeners":SomeController.listeners.grid, ...}
  json = @config.to_json.gsub(/\"(listeners|handler|scope)\":\s?\"([a-zA-Z\.\[\]\(\)]+)\"/, '"\1":\2')
  "Ext.ComponentMgr.create(#{json});"
end

Notice how it performs a gsub upon the rendered json-string, removing double-quotes from all values having the keys listeners, handler and scope. Let’s try it out –create a new view-action in your projects_controller named app/view/projects/step3.html.erb

<div id="panel"></div>
 
<%
    panel = extjs_component(
        "xtype" => 'panel',
        "iconCls" => "silk-application",
        "title" => 'Auto-generated Ext.Panel!',
        "renderTo" => 'panel',
        "frame" => true,
        "width" => 300,
        "height" => 100,
        "html" => 'Why are my feet so far from my head?',
        "collapsible" => true,
        "buttonAlign" => "center",
        "buttons" => [{
            "text" => "Save",
            "iconCls" => "silk-disk",
            "handler" => "Controller.onSave"
        }, {
            "text" => "Cancel",
            "iconCls" => "silk-cancel",
            "handler" => "Controller.onCancel"
        }]
    )
 
    extjs_onready(panel)
%>
 
<%= extjs_render %>
 
<script>
    Controller = function() {
        return {
            onSave : function() {
                Ext.Msg.alert('Controller says', 'You clicked Save');
            },
            onCancel : function() {
                Ext.Msg.alert('Controller says', 'You clicked Cancel');
            }
        }
    }();
 
</script>

Check out /projects/step3. Notice here how we’ve defined a simple singleton named Controller where we’ve housed the button-handlers for our panel. Again, take a look at the rendered json /projects/step3. Notice how the handler configuration-property has had its double-quotes removed. This technique will work for the listeners configuration-parameter as well.

Ext.onReady(function() {
    Ext.ComponentMgr.create({
        "html": "Why are my feet so far from my head?",
        "buttons": [
            {
                "text": "Save",
                "handler": Controller.onSave,  // --- double-quotes removed
                "iconCls": "silk-disk"
            },
            {
                "text": "Cancel",
                "handler": Controller.onCancel, // --- double-quotes removed
                "iconCls": "silk-cancel"
            }
        ],
        "title": "Auto-generated Ext.Panel!",
        "items": [
 
        ],
        "frame": true,
        "height": 100,
        "xtype": "panel",
        "buttonAlign": "center",
        "collapsible": true,
        "width": 300,
        "renderTo": "panel",
        "iconCls": "silk-application"
    });
});

Step 4: Setting the Stage with an Ext.Window

We’ll start to build up the stage for this simple projects app now by rendering an Ext.Window having layout: "border" with regions west and center. Create a new view named app/views/projects/step4.html.erb as follows:

<%
    window = extjs_component(
        "xtype" => "window",
        "id" => "projects",
        "title" => "Project Manager",
        "iconCls" => "silk-calendar",
        "closeAction" => "hide",
        "layout" => "border",
        "height" => 480,
        "width" => 800
    )
    window.add(extjs_component(
        "xtype" => "panel",
        "region" => "west",
        "width" => 300,
        "margins" => "5 5 5 5",
        "title" => "West"
    ))
    window.add(extjs_component(
        "xtype" => "panel",
        "id" => "workspace",
        "title" => "Center",
        "margins" => "5 5 5 0",
        "region" => "center",
        "layout" => "card",
        "activeItem" => 0,
        "layoutConfig" =>{
            "layoutOnCardChange" => true
        }
    ))
 
    extjs_onready(window)
%>
 
<%= extjs_render %>
 
<script>
 
    Ext.onReady(function() {
        var projects = Ext.getCmp('projects');
        new Ext.Toolbar({
            renderTo: 'nav',
            items: [{
                text: 'Projects',
                iconCls: 'silk-calendar',
                handler: function(btn, ev) {
                    projects.show(btn.el);
                }
            }, '-', '->', 'Logged in as Your Mother (<a href="#">Logout</a>)']
        });
    });
 
</script>

Browsing to /projects/step4, you should see a toolbar rendered–click the [Projects] button.

NOTE: If you get the following error, you’re experiencing cross-talks with JSON libraries.

wrong number of arguments (1 for 0)

This issues seems to be fixed with Rails version 2.3.4.

Moving right-along then, notice that we can define nested layouts using the add method of ExtJS::Component, just like its javascript cousin Ext.Component.

Step 5: Orgainizing your components into partials

One of the powerful feature of the extjs-mvc is that you can very easily add nested components to some container component by using partials. Create a new controller named users.

$ script/generate controller users

Now create a simple partial app/views/users/_foo.html.erb.

<% extjs_component(
    :container => container,
    "xtype" => 'panel',
    "title" => "I was rendered from the partial views/users/_foo.html.erb"
) %>

Note the addition of the parameter :container here, which I’ll soon explain.

Create a new erb template /views/projects/step5.html.erb.

<%
    window = extjs_component(
        "xtype" => "window",
        "id" => "projects",
        "title" => "Project Manager",
        "iconCls" => "silk-calendar",
        "closeAction" => "hide",
        "layout" => "border",
        "height" => 480,
        "width" => 800
    )
    window.add(extjs_component(
        "xtype" => "panel",
        "region" => "west",
        "width" => 300,
        "margins" => "5 5 5 5",
        "title" => "West"
    ))
    center = window.add(extjs_component(
        "xtype" => "panel",
        "id" => "workspace",
        "title" => "Center",
        "margins" => "5 5 5 0",
        "region" => "center",
        "layout" => "card",
        "activeItem" => 0,
        "layoutConfig" =>{
            "layoutOnCardChange" => true
        }
    ))
    extjs_onready(window)
%>
 
<%= center.add(:partial => "/users/foo.html.erb",
    "html" => "HTML content for the partial"
) %>
 
<%= extjs_render %>
 
<script>
    Ext.onReady(function() {
        var projects = Ext.getCmp('projects');
        new Ext.Toolbar({
            renderTo: 'nav',
            items: [{
                text: 'Projects',
                iconCls: 'silk-calendar',
                handler: function(btn, ev) {
                    projects.show(btn.el);
                }
            }, '-', '->', 'Logged in as Your Mother (<a href="#">Logout</a>)']
        });
    });
</script>
 
<%= extjs_render %>

See /projects/step5. The only difference between step5 and step4 is the addition of the following line:

<%= center.add(:partial => "/users/foo.html.erb",
    "html" => "HTML content for the partial"
) %>

It’s important to realize that when adding a partial to a containing component that the line be rendered in a separate block as you normally render a partial in order for the content to be captured into the main view.

Let’s have a brief look at the method ExtJS::Component#add in the extjs-mvc gem to see what’s happening here.

def add(*config)
    options = config.extract_options!
    if !options.keys.empty?
      if url = options.delete(:partial)
        # rendering a partial, cache the config until partial calls #add method.  @see else.
        @partial_config = options
        return @controller.render(:partial => url, :locals => {:container => self})
      else
        options.merge!(@partial_config) unless @partial_config.nil?
        options[:controller] = @controller unless @controller.nil?
        cmp = ExtJS::Component.new(options)
        @partial_config = nil
        @config[:items] << cmp
        return cmp
      end
    elsif !config.empty? && config.first.kind_of?(ExtJS::Component)
      cmp = config.first
      cmp.apply(@partial_config) unless @partial_config.nil?
      @partial_config = nil
      @config[:items] << cmp.config
      return cmp
    end
  end

Note when the :partial parameter is detected, the Rails partial method is called upon the @controller instance variable in the standard manner adding the local partial-variable :container set with a reference to the containing component. Also note how any extra component-configuration params are stored in the instance variable @partial_config. These params will automatically be applied when the partial creates a new component using the helper method extjs_component. Reviewing /view/users/_foo.html.erb:

<% extjs_component(
    :container => container,
    "xtype" => 'panel',
    "title" => "I was rendered from the partial views/users/_foo.html.erb"
) %>

The configuration-params xtype and title are the @partial_config. These params get applied to the component rendered in the partial in the same manner that Ext.apply(config, partialConfig); works.

Step 6: Creating your Model, Controller, and Stores

The extjs-mvc gem contains an ActiveRecord mixin named ExtJS::Model (DataMapper to come…). Include the mixin into any Model you wish to display in an Ext Store. ExtJS::Model contains two class-methods #extjs_fields, #extjs_record and one instance method #to_record. Use the class-method #extjs_fields to define those model attributes which will be used to compose an Ext.data.Record.

Let’s create a User model now:

$ script/generate model user first:string last:string email:string

We’ll tweak the user migration a bit to add some NOT NULL columns:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :first, :null => false
      t.string :last, :null => false
      t.string :email, :null => false
      t.timestamps
    end
  end
 
  def self.down
    drop_table :users
  end
end

Go ahead anddb:migrate.

Map the users resource in routes.rb

ActionController::Routing::Routes.draw do |map|
  map.resources :users
  .
  .
  .
end

Include the mixin ExtJS::Model into the User model in app/models/user.rb

class User < ActiveRecord::Base
  include ExtJS::Model
  extjs_fields :id, :first, :last, :email, :updated_at, :created_at
  #OR extjs_fields :exclude => [:id]    A Bug in Ext-3.0.1 requires id as a field currently
end

In script/console, take a quick look at what the class-method ExtJS::Model#extjs_record provides.

$ script/console
Loading development environment (Rails 2.3.4)
>> User.extjs_record
=> {
    "fields"=>[
          {:type=>:string, :allowBlank=>false, :name=>"first"},
          {:type=>:string, :allowBlank=>false, :name=>"last"},
          {:type=>:string, :allowBlank=>false, :name=>"email"},
          {:type=>:datetime, :allowBlank=>true, :name=>"created_at", :dateFormat=>"c"},
          {:type=>:datetime, :allowBlank=>true, :name=>"updated_at", :dateFormat=>"c"}
    ],
    "idProperty"=>"id"
}
>>

Nice. An auto-generated Ext.data.Record definition already. You shouldn’t need to use this method directly though–you’ll render your Store with a View-helper method instead–but it’s interesting to see how this works. I must stress that the extjs-mvc gem is the result of just two days of hacking. I’m hoping some full-time Rubyists might offer suggestions on improving the current implementation or contribute to the Git project.

While we’re in script/console, let’s create a few sample users.

>>User.create(:first => 'Caroline', :last => 'Schnapp', :email => 'caro@schnapp.com')
.
.
.

Creating your Controller

The extjs-mvc gem provides an ActionController mixin named ExtJS::Controller as well as an ActionView helper called ExtJS::Helpers::Store. Let’s modify our projects_controller a bit now.

class ProjectsController < ApplicationController
  include ExtJS::Controller
  helper ExtJS::Helpers::Store
  helper ExtJS::Helpers::Component
  .
  .
  .
end

Let’s move on to users_controller. Add a simple index action which grabs all the users.

class UsersController < ApplicationController
    include ExtJS::Controller
 
    def index
        rs = User.all.collect {|u| u.to_record}
        render(:json => {:success => true, :data => rs})
    end
end

The method #to_record come from the ExtJS::Model mixin that we included into User. #to_record uses the fields defined by the class-method ExtJS::Model#extjs_fields to decide which attributes to return.

class User < ActiveRecord::Base
    include ExtJS::Model
    extjs_fields :id, :first, :last, :email
end

There’s nothing mysterious going on in ExtJS::Model#to_record; it attaches the primary_key in addition to all the fields defined by extjs_fields.

def to_record
  data = {self.class.primary_key => self.send(self.class.primary_key)}
  self.class.extjs_record_fields.each do |f|
    if refl = self.class.reflections[f]
      if refl.macro === :belongs_to
        data[f] = self.send(f).to_record
      elsif refl.macro === :has_many
        data[f] = self.send(f).collect {|r| r.to_record}  # <-- careful with this one!
      end
    else
      data[f] = self.send(f)
    end
  end
  data
end

#extjs_record will check for any :belongs_to and :has_many relationships and recursively gather their attributes as well.

Auto-generating a Store

Ok, we’re just about ready to get into the fun stuff. Let’s create a new users partial named /views/users/_grid.html.erb and render it into our projects_controller. The users_controller is not going to be routed-to at all in the traditional manner — instead, it’ll act more like a json-service to our application entry-point, the projects_controller. I like to use partials named “grid” and “form” to render the UI components for each Controller. Go ahead and create the partial /views/users/_grid.html.erb.

<%= javascript_include_tag "app/users/UsersGrid" %>
 
<%= extjs_store(
    :controller => "users",
    :writer => {"encode" => false},
    :config => {
        "autoLoad" => true,
        "autoSave" => true,
        "restful" => true
    }
).render %>
 
<% extjs_component(
    :container => container,
    "xtype" => 'users-grid',
    "storeId" => 'user',
    "title" => "Users",
    "iconCls" => 'silk-group'
) %>

The method extjs_store is provided by the helper ExtJS::Helpers::Store. This method will return an instance of a plain-old-ruby-object named ExtJS::Data::Store. This object accepts identical configuration-properties as its javascript cousin, Ext.data.Store through the :config parameter. The :controller configuration-param is most important though — this parameter will constantize its related model and inspect its column meta-data in order to render an Ext.data.DataReader. If your model cannot be inflected based upon controller-name, simply provide the :model parameter. The Ext.data.DataReader meta-information parameters such as root, successProperty and messageProperty can be defined using controller class-methods provided by ExtJS::Component

class UsersController < ApplicationController::Base
  include ExtJS::Controller
  helper ExtJS::Helpers::Store
 
  extjs_root :records  # defaults to :data
  extjs_success_property :shes_good_eh  #defaults to :success
  extjs_message_property :msg  # defaults to :message
end

Note: I think I’ll scrap the :config parameter and just place all config-options on the same level, simply discriminating between Ext-parameters by symbolized params.

Or application-wide by modifying the class ExtJS::MVC in something like environment.rb:

ExtJS::MVC.success_property = :success_o_rama
ExtJS::MVC.root = :rooty_toot
ExtJS::MVC.message_property = :une_message_pour_vous

Moving right along then, notice this partial includes a javascript file named app/users/UserGrid. We’ll create this file next but first we must create a few directories in public/javascripts. I like to organize my javascript in a similar directory structure as Rails/Merb’ /app directory (The extjs-mvc Gem could use some generators if anyone has any ideas).

$ mkdir public/javascripts/app
$ mkdir public/javascripts/app/projects
$ mkdir public/javascripts/app/users

Now create the following javascript file /javascripts/app/users/UsersGrid.js.

Ext.ns("users");
/**
 * @class users.Grid
 */
users.Grid = Ext.extend(Ext.grid.EditorGridPanel, {
    buttonAlign: "center",
    autoExpandColumn: 0,
 
    initComponent: function(){
        this.store = Ext.StoreMgr.get(this.storeId);
        this.columns = this.buildColumns();
        this.tbar = this.buildUI();
 
        this.viewConfig = {
            forceFit: true
        };
 
        users.Grid.superclass.initComponent.call(this);
    },
 
    buildUI : function() {
        return [{
            text: "Add",
            iconCls: "silk-add",
            handler: this.onAddRecord,
            scope: this
        }, "-", {
            text: "Remove",
            iconCls: "silk-delete",
            handler: this.onRemoveRecord,
            scope: this
        }, "-"];
    },
 
    buildColumns : function() {
        return [{
            header: "First",
            dataIndex: "first",
            editor: new Ext.form.TextField({})
        }, {
            header: "Last",
            dataIndex: "last",
            editor: new Ext.form.TextField({})
        }, {
            header: "Email",
            dataIndex: "email",
            editor: new Ext.form.TextField({})
        }];
    },
 
    onAddRecord: function(){
        var rec = new this.store.recordType({});
        this.store.insert(0, rec);
        this.startEditing(0, 0);
    },
 
    onRemoveRecord: function(){
        var index = this.getSelectionModel().getSelectedCell();
        if (!index) {
            return false;
        }
        var rec = this.store.getAt(index[0]);
        this.store.remove(rec);
    }
});
Ext.reg("users-grid", users.Grid);

Finally, wire-up the partial in a new view-template /views/projects/step6.html.erb

<%
    window = extjs_component(
        "xtype" => "window",
        "id" => "projects",
        "title" => "Project Manager",
        "iconCls" => "silk-calendar",
        "closeAction" => "hide",
        "layout" => "border",
        "height" => 480,
        "width" => 800
    )
 
    window.add(extjs_component(
        "xtype" => "panel",
        "id" => "workspace",
        "title" => "Center",
        "margins" => "5 5 5 0",
        "region" => "center",
        "layout" => "card",
        "activeItem" => 0,
        "layoutConfig" =>{
            "layoutOnCardChange" => true
        }
    ))
    extjs_onready(window)
%>
 
<%= window.add(:partial => '/users/grid',
    "region" => 'west',
    "width" => 300,
    "margins" => '5 5 5 5',
    "cmargins" => '5 5 5 5',
    "collapsible" => true
) %>
 
<%= extjs_render %>
 
<script>
    Ext.onReady(function() {
        var projects = Ext.getCmp('projects');
        new Ext.Toolbar({
            renderTo: 'nav',
            items: [{
                text: 'Projects',
                iconCls: 'silk-calendar',
                handler: function(btn, ev) {
                    projects.show(btn.el);
                }
            }, '-', '->', 'Logged in as Your Mother (<a href="#">Logout</a>)']
        });
    });
</script>
 
<%= extjs_render %>

Ok, restart your server and navigate to /projects/step6. If you don’t see any rows in your grid, check the NET tab in Firebug: there should be a successful GET request reported. Inspect the response&mdashIs the success property true? Did you add any records in script/console?

$ User.create(:first => "Hunky", :last => "Bill", :email => "hunkybill@perogie.com")

Try viewing the HTML source-code to have a look at the auto-generated JsonStore.

new Ext.data.JsonStore({
    "restful": true,
    "autoSave": true,
    "url": "/users.json",
    "fields": [
        {
            "type": "int",
            "allowBlank": true,
            "name": "id"
        },
        {
            "type": "string",
            "allowBlank": false,
            "name": "first"
        },
        {
            "type": "string",
            "allowBlank": false,
            "name": "last"
        },
        {
            "type": "string",
            "allowBlank": false,
            "name": "email"
        },
        {
            "type": "date",
            "allowBlank": true,
            "dateFormat": "c",
            "name": "created_at"
        },
        {
            "type": "date",
            "allowBlank": true,
            "dateFormat": "c",
            "name": "updated_at"
        }
    ],
    "messageProperty": "message",
    "root": "data",
    "successProperty": "success",
    "idProperty": "id",
    "storeId": "user",
    "autoLoad": true,
    writer: new Ext.data.JsonWriter({
        "encode": false
    })
});

RESTful, Writable Stores

Ext-3.0 introduced new data-package features for automating the process of writing record-data back to the server. In the past, this had to be done by manually composing Ajax requests. With a writer-enabled Store, one interacts with the Store and its records using the same Ext-2.0 API except Ajax requests will be automatically executed. For example, the following code will all result in Ajax requests to the server:

var record = store.getAt(0);   // Grab the first record.
record.set('email', 'foo@bar.com');   // PUT
store.remove(record);   // DELETE
new_record = new store.recordType({first:'chris', last: 'scott', email:'chris@scott.com'});
store.add(new_record);   // POST

Creating a Writable store is very easy — simply plug an instance of Ext.data.JsonWriter or Ext.data.XmlWriter into your Store just as you would a JsonReader/XmlReader. With the Store helper included in the extjs-mvc gem, it’s even easier.

<script>
<%= extjs_store({
    :controller => "users",
    :writer => {
      :encode => false
    },
    :config => {
        "autoLoad" => true,
        "autoSave" => true,  #<-- Enable cell-level updates
        "restful" => true,  # <-- RESTful mode        
    }
}) %>

If you try interacting with the grid from /projects/step6, you should see Ajax requests firing all over the place. They’ll all be 404 currently since we haven’t implemented any write-actions in the users_controller. Let’s do that now.

class UsersController < ApplicationController
    include ExtJS::Controller
 
    def index
        rs = User.all.collect {|u| u.to_record}
        render(:json => {:success => true, :data => rs})
    end
 
    def create
        u = User.create(params["data"])
        render(:json => {:success => true, :data => u.to_record})
    end
 
    def update
        u = User.find(params[:id])
        render(:text => '', :status => (u.update_attributes(params["data"])) ? 204 : 500)
    end
 
    def destroy
        u = User.find(params[:id])
        render(:text => '', :status => (u.destroy) ? 204 : 500)
    end
end

Refresh your app and try interacting with /projects/step6 once again (double-click rows to edit; use the [Add] and [Remove] buttons on the top-toolbar). These actions are pretty weak on error-checking but you get the idea — automated Stores.

Step 7: Adding a Direct Router

Rails/Merb Ext.Direct support is available through the gems rails-extjs-direct and merb-extjs-direct hosted at Rubyforge.

$ sudo gem install rails-extjs-direct
$ sudo gem install merb-extjs-direct

However, I’m currently in the process of moving these gems to Github intead. I’m also thinking about combining both the Merb and Rails gems into a single gem named extjs-direct then sniffing which framework they’re being included into, including appropriate files for each.

The Rails Ext.Direct gem is implemented with Rack so the first step is to plug it in as middleware in environment.rb after configuring the rails-extjs-direct gem:

Rails::Initializer.run do |config|
  config.gem "extjs-mvc"
  config.gem "rails-extjs-direct"
  config.middleware.use "Rails::ExtJS::Direct::RemotingProvider", "/direct"
  .
  .
  .
end

The parameter “/direct” tells the RemotingProvider to handle all requests coming in on that url.

Next, include the Rails::ExtJS::Direct::Controller mixin into each controller you wish to be “Directable”. We’ll create a new controller now called tasks along with an associated model Task.

$ script/generate controller tasks
$ script/generate model task title:string description:text user_id:integer

Again, tweak the migration slightly to add some NOT NULL columns.

class CreateTasks < ActiveRecord::Migration
  def self.up
    create_table :tasks do |t|
      t.integer :user_id, :null => false
      t.string :title, :null => false
      t.text :description, :null => false
      t.timestamps
    end
  end
 
  def self.down
    drop_table :tasks
  end
end

Do a db:migrate.

Add the ExtJS::Model mixin to the new Task model.

class Task < ActiveRecord::Base
    include ExtJS::Model
    extjs_fields :id, :user_id, :user, :title, :description
 
    belongs_to :user
end

Notice how we’ve added the belongs_to association :user to the list of extjs_fields.

Next, include Ext.Direct controller Mixin Rails::ExtJS::Direct::Controller which provides the class-method #direct_actions. This list should be specified in the specific order of C,R,U,D to help map to the Proxy API.

class TasksController < ApplicationController
  include Rails::ExtJS::Direct::Controller
  include ExtJS::Controller
  helper ExtJS::Helpers::Store
 
  direct_actions :create, :load, :update, :destroy
  #                  C        R        U        D
  def load
    @xresponse.result = {
      :data => Task.all.collect {|u| u.to_record}
    }
    @xresponse.status = true
    render :json => @xresponse
  end
 
  def create
    @xresponse.status = true
    @xresponse.message = "Created Task"
    render :json => @xresponse
  end
 
  def update
    @xresponse.status = true,
    @xresponse.message = "Updated Task"
    render :json => @xresponse
  end
 
  def destroy
    @xresponse.status = true
    @xresponse.message = "Destroyed Task"
    render :json => @xresponse
  end
end

The instance variable @xrequest and @xresponse are provided by the Rails::ExtJS::Direct::Controller using a :before_filter. The @xresponse variable contains all the parameters sent by the client-side request, as seen in Firebug. The @xresponse variable must always be returned via render(:json => @xresponse)

Ok, now that we’ve got some actions defined, let’s configure our client-side DirectProvider. Start by creating a new tasks partial named views/tasks/_direct.html.erb. We’ll create our direct-provider by hand to test it out and auto-generate it later.

<% extjs_component(:container => container,
    "xtype" => "panel",
    "html" => "<p>In Firebug, try exploring ..."
) %>
 
<script>
Ext.Direct.addProvider({
    url: '/direct',    // --- There's our direct-url again
    type: 'remoting',
    actions: {
        Tasks: [{    // --- There's our controller and actions
            name: 'load',
            len: 1
        }, {
            name: 'update',
            len: 1
        }, {
            name: 'create',
            len: 1
        }, {
            name: 'destroy',
            len: 1
        }]
    }
});
</script>

Finally, wire-up the partial /views/tasks/_direct.html.erb as a new projects_controller view in /views/projects/step7.html.erb.

<%
    window = extjs_component(
        "xtype" => "window",
        "id" => "projects",
        "title" => "Project Manager",
        "iconCls" => "silk-calendar",
        "closeAction" => "hide",
        "layout" => "border",
        "height" => 480,
        "width" => 800
    )
 
    workspace = window.add(extjs_component(
        "xtype" => "panel",
        "id" => "workspace",
        "border" => false,
        "margins" => "5 5 5 0",
        "region" => "center",
        "layout" => "card",
        "activeItem" => 0,
        "layoutConfig" =>{
            "layoutOnCardChange" => true
        }
    ))
    extjs_onready(window)
%>
 
<!-- /users/grid -->
<%= window.add(:partial => '/users/grid',
    "region" => 'west',
    "width" => 300,
    "margins" => '5 5 5 5',
    "cmargins" => '5 5 5 5',
    "collapsible" => true
) %>
 
<!-- /tasks/direct -->
<%= workspace.add(:partial => '/tasks/direct',
    "title" => "Wiring up a DirectProvider by hand",
    "bodyStyle" => "padding: 10px"
) %>
 
<%= extjs_render %>
 
<script>
    Ext.onReady(function() {
        var projects = Ext.getCmp('projects');
        new Ext.Toolbar({
            renderTo: 'nav',
            items: [{
                text: 'Projects',
                iconCls: 'silk-calendar',
                handler: function(btn, ev) {
                    projects.show(btn.el);
                }
            }, '-', '->', 'Logged in as Your Mother (<a href="#">Logout</a>)']
        });
    });
</script>
 
<%= extjs_render %>

Open /projects/step7 and type the controller-name Tasks into the Firebug console. You should see an Object containing the four methods load, update, create, destroy as defined above. Try executing these methods in the console:

$ Tasks.load({});
$ Tasks.create({});
$ Tasks.update({});
$ Tasks.destroy({});

Notice how the http-protocol has been completely abstracted. No more Ext.Ajax.request. This is not unlike interacting with your models in irb console. In Firebug console, observe what happens when you execute multiple direct-transactions in one line:

$ Tasks.load();Tasks.create();Tasks.destroy();Tasks.update();Tasks.load();

These simultaneous Direct-transactions within a configurable duration will be queued into the same Ajax request.

Step 8: Auto-generating the direct-provider

Well, that’s all pretty neat but how to centralize this and auto-generate it? How will each rendered partial inform the parent controller (inventory_controller in our case) that it has an API to add to the Ext.Direct RemotingProvider? What we want to do is somehow hand our controller to some centralized object and let it deal with rendering the actual javascript. Let’s go to the projects_controller and add the Rails::ExtJS::Direct::Controller mixin to it.

class ProjectsController < ApplicationController
  include ExtJS::Controller
  include Rails::ExtJS::Direct::Controller
  helper ExtJS::Helpers::Store
  helper ExtJS::Helpers::Component
  .
  .
  .
end

Create a new projects_controller view-template /views/projects/step8.html.erb and use the helper method get_extjs_direct_provider to create an Ext.Direct provider instance available throughout all the partials:

<% @provider = get_extjs_direct_provider("remoting", "/direct") %>
 
<%
    window = extjs_component(
        "xtype" => "window",
        "id" => "projects",
        "title" => "Project Manager",
        "iconCls" => "silk-calendar",
        "closeAction" => "hide",
        "layout" => "border",
        "height" => 480,
        "width" => 800
    )
 
    workspace = window.add(extjs_component(
        "xtype" => "panel",
        "id" => "workspace",
        "border" => false,
        "margins" => "5 5 5 0",
        "region" => "center",
        "layout" => "card",
        "activeItem" => 0,
        "layoutConfig" =>{
            "layoutOnCardChange" => true
        }
    ))
    extjs_onready(window)
%>
 
<!-- /users/grid -->
<%= window.add(:partial => '/users/grid',
    "itemId" => 'users-grid',
    "region" => 'west',
    "width" => 300,
    "margins" => '5 5 5 5',
    "cmargins" => '5 5 5 5',
    "collapsible" => true
) %>
 
<!-- /tasks/grid -->
<%= workspace.add(:partial => '/tasks/grid',
    "itemId" => 'tasks-grid'
) %>
 
<!-- Render the Direct provider after all partials have run -->
<%= @provider.render %>
 
<%= extjs_render %>
 
<script>
    Ext.onReady(function() {
        var projects = Ext.getCmp('projects');
        new Ext.Toolbar({
            renderTo: 'nav',
            items: [{
                text: 'Projects',
                iconCls: 'silk-calendar',
                handler: function(btn, ev) {
                    projects.show(btn.el);
                }
            }, '-', '->', 'Logged in as Your Mother (<a href="#">Logout</a>)']
        });
    });
</script>
 
<%= extjs_render %>

Note how the @provider must be manually rendered after all partials have been rendered:

<%= @provider.render %>
<%= extjs_render %>

Create a new tasks partial named /views/tasks/_grid.html.erb and add its controller API to the @provider. We’ll also include a new javascript file /javascripts/app/tasks/TasksGrid.js.

<%= javascript_include_tag ("app/tasks/TasksGrid") %>
<% @provider.add_controller("tasks") %>
 
<% @store = extjs_store(
    :controller => "tasks",
    :proxy => 'direct',
    :writer => {:encode => false},
    :config => {
        "autoLoad" => true,
        "restful" => true
    }
) %>
 
<%= @store.render %>
 
<% extjs_component(
    :container => container,
    "xtype" => 'tasks-grid',
    "title" => 'Tasks',
    "iconCls" => "silk-date",
    "storeId" => @store.id
) %>

Notice that creating a direct-enabled store with the helper-method extjs_store is as simple as setting the configuration-parameter :proxy => "direct".

Finally, create the javascript file /javascripts/app/tasks/TasksGrid.js:

Ext.ns("tasks");
tasks.Grid = Ext.extend(Ext.grid.GridPanel, {
    autoExpandColumn: 1,
    initComponent: function(){
        this.store = Ext.StoreMgr.get(this.storeId);
        this.columns = [{
            header: "Title",
            dataIndex: "title",
            editor: new Ext.form.TextField({})
        }, {
            header: "Description",
            dataIndex: "description",
            editor: new Ext.form.TextField({})
        }, {
            header: "User",
            dataIndex: "user",
            renderer: function(user) {  // --- custom-renderer for belongs_to :user
                return user.first + " " + user.last;
            }
        }];
        this.tbar = [{
            text: "Add",
            iconCls: "silk-add",
            handler: this.onAddRecord,
            scope: this
        }, "-", {
            text: "Remove",
            iconCls: "silk-delete",
            handler: this.onRemoveRecord,
            scope: this
        }, "-"];
 
        this.viewConfig = {
            forceFit: true
        };
 
        this.addEvents("add-record");
 
        tasks.Grid.superclass.initComponent.call(this);
    },
 
    onAddRecord: function(){
        this.fireEvent("add-record", this);
    },
 
    onRemoveRecord: function(){
        var record = this.getSelectionModel().getSelected();
        this.store.remove(record);
    }
});
Ext.reg("tasks-grid", tasks.Grid);

Restart your server and browse to /projects/step8.

Step 9: Finishing up with a Tasks Form

What’s an app without a form? Let’s finish up this tutorial by implementing a simple Tasks form. Create a new partial /views/tasks/_form.html.erb.

<%= javascript_include_tag "ux/FormPanel/Storable/Ext.ux.FormPanel.Storable" %>
 
<%
    extjs_component(
        :container => container,
        "title" => "Task",
        "xtype" => 'form',
        "plugins" => {
            "ptype" => "form-storable",
            "storeId" => "task",
            "saveButton" => "buttons.btn-save",
            "cancelButton" => "buttons.btn-cancel"
        },
        "labelAlign" => 'top',
        "bodyStyle" => "padding:10px",
        "buttonAlign" => "center",
        "buttons" => [{
            "text" => "Save",
            "itemId" => "btn-save"
        }, {
            "text" => "Cancel",
            "itemId" => "btn-cancel"
        }],
        :items => [{
            "xtype" => 'textfield',
            "anchor" => '100%',
            "fieldLabel" => 'title',
            "allowBlank" => false,
            "name" => 'title'
        }, {
            "xtype" =>'combo',
            "store" => "user",
            "anchor" => '100%',
            "fieldLabel" => 'Assigned to',
            "storeId" => 'user',
            "hiddenName" => 'user_id',
            "displayField" => 'email',
            "valueField" => 'id',
            "mode" => 'local',
            "forceSelection" => true,
            "triggerAction" => 'all'
        }, {
            "xtype" => 'textarea',
            "fieldLabel" => 'Description',
            "grow" => true,
            "allowBlank" => false,
            "growMin" => 200,
            "anchor" => '100%',
            "name" => 'description'
        }]
    )
%>

Here we’re rendering a vanilla Ext.form.FormPanel using the "xtype" => "form" configuration. The extjs-mvc Gem does not currently have any form-building features but I’m open to any ideas on how to pursue this. Take note of the second field having "xtype" => "combo" in the :items array — Notice how it connects the user Store to itself ("store" => "user") — this references the same Store used for the UsersGrid in the window’s west region.

Also note the form is using a plugin created for this tutorial named form-storable. This plugin is a bit long but don’t be discouraged–it’s built to work with any FormPanel you wish to bind to a Store and saves your from having to write the same Store-binding code over and over again. DRY, right? It’s also a good example of how to organize your own custom plugins into a ux directory-structure.

First create the ux directory-structure to house the plugin:

$ mkdir public/javascripts/ux
$ mkdir public/javascripts/ux/FormPanel
$ mkdir public/javascripts/ux/FormPanel/Storable

Now create the file public/javascripts/ux/FormPanel/Storable/Ext.ux.FormPanel.Storable.js.

Ext.ns("Ext.ux", "Ext.ux.FormPanel");
/**
 * @class tasks.FormPanel
 * A plugin which adds Store/Record binding methods to a FormPanel instance.
 */
Ext.ux.FormPanel.Storable = function(param) {
    Ext.apply(this, param);
}
Ext.ux.FormPanel.Storable.prototype = {
    /**
     * @cfg {String} storeId ID of Store instance to bind to panel.
     */
    storeId: undefined,
    /**
     * @cfg {String} saveButton (optional) Specifiy a dot-separted string where the left-hand-side specifies the button
     * locations [tbar|bbar|buttons] and the right-hand-side specifies the button"s itemId.
     * eg:  saveButton: "tbar.btn-save", "bbar.btn-save", "buttons.btn.save".  When the save button is located,
     * it will have an automated save-handler applied to it from Ext.ux.FormPanel.Storable.InstanceMethods#onStorableSave
     */
    saveButton: undefined,
     /**
     * @cfg {String} cancelButton (optional) Specifiy a dot-separted string where the left-hand-side specifies the button
     * locations [tbar|bbar|buttons] and the right-hand-side specifies the button"s itemId.
     * eg:  cancelButton: "tbar.btn-cancel", "bbar.btn-cancel", "buttons.btn.cancel".  When the cancel button is located,
     * it will have an automated cancel-handler applied to it from Ext.ux.FormPanel.Storable.InstanceMethods#onStorableCancel
     */
    cancelButton: undefined,
 
    init : function(panel) {
        // mixin InstanceMethods
        Ext.apply(panel, Ext.ux.FormPanel.Storable.InstanceMethods.prototype);
 
        panel.bind(Ext.StoreMgr.lookup(this.storeId));
 
        if (this.saveButton) {
            this.setHandler("save", this.saveButton, panel);
        }
        if (this.cancelButton) {
            this.setHandler("cancel", this.cancelButton, panel);
        }
 
        panel.addEvents(
            /**
             * @event storable-save
             */
            "storable-save",
            /**
             * @event storable-cancel
             */
            "storable-cancel"
        );
    },
 
    setHandler : function(action, info, panel) {
        var ids = info.split(".");
        var btn = undefined;
        if (ids[0] === "buttons") {
            for (var n = 0, len = panel.buttons.length; n < len; n++) {
                if (panel.buttons[n].itemId === ids[1]) {
                    btn = panel.buttons[n];
                    break;
                }
            }
        } else {
            var pos = (ids[0] === "tbar") ? "Top" : "Bottom";
            var toolbar = this["get" + pos + "Toolbar"]();
            btn = toolbar.getComponent(ids[1]);
        }
        if (!btn) {
            throw new Error("Ext.ux.FormPanel.Storable failed to find button " + ids[1] + " on " + ids[0]);
        }
        btn.setHandler(panel["onStorable"+ Ext.util.Format.capitalize(action)].createDelegate(panel));
    }
};
 
/**
 * @class Ext.ux.FormPanel.Storable.InstanceMethods
 * Mixin for FormPanel
 */
Ext.ux.FormPanel.Storable.InstanceMethods = function() {};
Ext.ux.FormPanel.Storable.InstanceMethods.prototype = {
 
    storableMask: undefined,
 
    /**
     * binds an Ext.data.Store to the Panel
     * @param {Ext.data.Store} store
     */
    bind : function(store) {
        // bind a Store to the form.  Fire the "form-save" event when write-actions are successful
        this.store = store;
 
        // Add store-listeners to show/hide load-mask.
        this.store.un("beforewrite", this.onStorableBeforeWrite, this);
        this.store.on("beforewrite", this.onStorableBeforeWrite, this);
 
        this.store.un("write", this.onStorableWrite, this);
        this.store.on("write", this.onStorableWrite, this);
 
    },
 
    /**
     * Loads a record.  Sets record pointer.  Sets title.
     * @param {Object} record
     */
    loadRecord: function(record) {
        this.record = record;
        this.getForm().loadRecord(record);
 
        // TODO:  Need to be able to customize title.
        this.setTitle("Edit Record");
    },
    /**
     * Resets underlying BasicForm.  Nullifies record pointer.
     */
    reset: function() {
        this.record = null;
        this.getForm().reset();
 
        // TODO:  Need to customize this.
        this.setTitle("Create Record");
    },
 
    // private
    onStorableBeforeWrite : function(proxy, action) {
        if (!this.mask) {
            this.mask = new Ext.LoadMask(this.el, {});
        }
        // quick and dirty verb present-tense inflector.
        var verb = (action[action.length-1] === "e") ? action.substr(0, action.length-1) + "ing" : action + "ing";
        this.mask.msg = verb + " record.  Please wait...";
        this.mask.show();
    },
 
    // private
    onStorableWrite : function(proxy, action) {
        this.mask.hide();
        this.fireEvent("storable-save", this);
    },
 
    // protected
    onStorableCancel : function(btn, ev) {
        this.fireEvent("storable-cancel", this, ev);
    },
 
    // protected
    onStorableSave : function(btn, ev) {
        var form = this.getForm();
        // First, validate the form...
        if (!form.isValid()) {
            Ext.Msg.alert("Error", "Form is invalid");
            return false;
        }
 
        // She"s all good.
        if (this.record === null) { // -- CREATE
            var Task = this.store.recordType;
            this.store.add(new Task(form.getValues()));
        } else {    // -- UPDDATE
            form.updateRecord(this.record);
        }
    }
};
 
Ext.preg("form-storable", Ext.ux.FormPanel.Storable);

Note how the plugin mixes several instance-methods into the FormPanel its plugged into, including #bind, #loadRecord, #reset, #storableOnSave, #storableOnCancel in addition to adding two events storable-save and storable-cancel. The nice thing about this plugin is that it saved us from having to make a custom FormPanel extension like TasksForm, for example. Plugins are not unlike ruby Mixins.

Ext.apply(panel, Ext.ux.FormPanel.Storable.InstanceMethods.prototype);

Let’s fill-out our direct-API in tasks_controller. Edit /controllers/tasks_controller.rb

class TasksController < ApplicationController
  include Rails::ExtJS::Direct::Controller
  include ExtJS::Controller
  helper ExtJS::Helpers::Store
 
  direct_actions :create, :load, :update, :destroy
  def load
    @xresponse.result = {
      :data => Task.all.collect {|u| u.to_record}
    }
    @xresponse.status = true
    render :json => @xresponse
  end
 
  def create
    data = params["data"] || params
    t = Task.create(data)
    @xresponse.result = t.to_record
    @xresponse.status = true
    @xresponse.message = "Created Task"
    render :json => @xresponse
  end
 
  def update
    data = params["data"] || params
    t = Task.find(data["id"])
    t.update_attributes(params)
    @xresponse.status = true
    @xresponse.message = "Updated Task"
    render :json => @xresponse
  end
 
  def destroy
    t = Task.find(params["id"])
    t.destroy
    @xresponse.status = true
    @xresponse.message = "Destroyed Task"
    render :json => @xresponse
  end
end

Note: Due to a bug in Ext.data.DirectProxy when using a Ext.data.JsonWriter, the POST params are not structured correctly. Thus each action in TasksController has to use this code:

    data = params["data"] || params

Once Ext-3.0.3 is released, the problem will be fixed and POST data will be placed into a key as specified by the JsonReader root property (eg: “data”).

Finally, create the last project_controller view, /views/projects/step9.html.erb.

<% @provider = get_extjs_direct_provider("remoting", "/direct") %>
 
<!-- build the application layout -->
<%
    window = extjs_component(
        "xtype" => "window",
        "id" => "projects",
        "title" => "Project Manager",
        "iconCls" => "silk-calendar",
        "closeAction" => "hide",
        "layout" => "border",
        "height" => 480,
        "width" => 800
    )
 
    workspace = window.add(extjs_component(
        "xtype" => "panel",
        "itemId" => "workspace",
        "border" => false,
        "margins" => "5 5 5 0",
        "region" => "center",
        "layout" => "card",
        "activeItem" => 0,
        "layoutConfig" =>{
            "layoutOnCardChange" => true
        }
    ))
    extjs_onready(window)
%>
 
<!-- partial:  /users/_grid -->
<%= window.add(:partial => '/users/grid',
    "itemId" => 'users-grid',
    "region" => 'west',
    "width" => 300,
    "margins" => '5 5 5 5',
    "cmargins" => '5 5 5 5',
    "collapsible" => true
) %>
 
<!-- partial:  tasks/_grid -->
<%= workspace.add(:partial => '/tasks/grid',
    "itemId" => 'tasks-grid',
    "listeners" => "ProjectsController.listeners.tasks.grid")
%>
 
<!-- partial: tasks/_form -->
<%= workspace.add(:partial => '/tasks/form',
    "itemId" => 'tasks-form',
    "listeners" => "ProjectsController.listeners.tasks.form"
) %>
 
 
 
<!-- Render the Direct provider after all partials have run -->
<%= @provider.render %>
 
<%= extjs_render %>
 
<script>
    ProjectsController = function() {
 
        var workspace = null;
        Ext.onReady(function() {
            var projects = Ext.getCmp('projects');
 
            // set workspace pointer for convenience.
            workspace = projects.getComponent('workspace');
 
            new Ext.Toolbar({
                renderTo: 'nav',
                items: [{
                    text: 'Projects',
                    iconCls: 'silk-calendar',
                    handler: function(btn, ev) {
                        projects.show(btn.el);
                    }
                }, '-', '->', 'Logged in as SomeOne (<a href="#">Logout</a>)']
            });
        });
 
        return {
            listeners: {
                tasks: {
                    grid: {
                        'add-record' : function(grid) {
                            var fpanel = workspace.getComponent('tasks-form');
                            fpanel.reset();
                            workspace.getLayout().setActiveItem(fpanel);
                        },
                        'rowdblclick' : function(grid, index, ev) {
                            var record = grid.store.getAt(index);
                            var fpanel = workspace.getComponent('tasks-form');
                            fpanel.loadRecord(record);
                            workspace.getLayout().setActiveItem(fpanel);
                        }
                    },
                    form : {
                        'storable-save' : function(fpanel) {
                            var grid = workspace.getComponent('tasks-grid');
                            workspace.getLayout().setActiveItem(grid);
                        },
                        'storable-cancel' : function(fpanel) {
                            var grid = workspace.getComponent('tasks-grid');
                            workspace.getLayout().setActiveItem(grid);
                        }
                    }
                }
            }
        }
    }();
</script>

See /projects/step9. Click the [Add] button to create a new Task and double-click grid-rows to edit. Important to note in this last example are the attached listeners from ProjectsController.listeners.task.*. Notice how we’re able to interact with all the components rendered through partials from the parent-controller, projects–our components are loosely coupled. Each partial is like an actor on a stage presented by projects_controller. Each actor is controlled by the attached listeners on ProjectsController.listeners.*.

Summary

With the relatively small extjs-mvc Gem, produced in just a few days, automated ExtJS Javascript component-rendering is now possible in a Rail/Merb friendly way. By taking advantage of the xtype mechanism introduced in Ext-2.0 along with the new ptype (plugin-type) mechanism in Ext-3.0, the entire library of ExtJS widgets can be rendered using a single plain-old-ruby-object ExtJS::Component, accessed through the helper-method extjs_component made available by the helper ExtJS::Helpers::Component . In addition, the new Writer features of Ext-3.0 are easily implemented in Rails/Merb by using the helper ExtJS::Helpers::Store, allowing one to implement both RESTful and Ext.Direct-enabled Stores.

The extjs-mvc is very new and in need of some help by a few full-time ruby-developers. It’s my hope that some of you out there will fork the Github project and help tidy things up. One interesting idea I had was to move the extjs_store method from the helper into an ActionController mixin, allowing one to define the Store in the controller. For example:

class TasksController < ApplicationController
    extjs_store :model => "task", :writer => {"encode" => false}
end

This could possibly allow for automated CRUD handling on any model without having to define the same CRUD actions over-and-over. An intelligent mixin could constantize the model defined on the extjs_store and provide :before_action, :after_action hooks.

Another idea to explore might be to move the component-configurations fed to the helper-method extjs_component from the view-templates into YAML files or the database. This would allow for easy configuration of the application-components through a backend interface. Also, components could be easily tweaked / plugins added based upon a user’s role/permissions.

A few other areas to explore would be form-building and grid-column-building helpers. So fork the Github Project. If you have some good ideas, send me a pull request.

Implementation Spotlight: Eaton’s Intelligent Power Manager

Monday, August 31st, 2009

I recently had an opportunity to sit down with the very talented developers at Eaton to discuss their recent software release. Read how Eaton’s superstars Jonathan Bonzy, Sebastien Volle, and their team implemented an amazing RIA for managing power with Ext JS.

Eaton Corporation is a diversified power management company, with 75,000 employees, that sells products to customers in more than 150 countries. Eaton’s electrical business is a global leader in power distribution, power quality, control and industrial automation products and services.

What is Intelligent Power?

Eaton’s Intelligent Power® Manager is a robust and scalable program that can substantially lower costs for supervising networked uninterruptible power systems (UPSs) and associated equipment compared to using major network management system platforms. The Ext JS Framework is used as a core UI component in Intelligent Power Manager where all views, boxes or grids are rendered through Ext methods.

Intelligent Power Manager main view with node list and details for selected node

Did you evaluate other JavaScript frameworks for the Intelligent Power Manager? Why did you choose Ext?

We compared Ext JS to many popular libraries (jQuery, Prototype, MooTools, Script.aculo.us, Dojo, Rialto, Qooxdoo, etc.). We found Ext JS to be fully open source with a strong community combined with great technical support from the authors themselves. The Ext JS cross-browser widgets set is huge, which allowed us to complete our entire interface for Intelligent Power Manager. Moreover, Ext JS’s small footprint and low memory consumption surpassed our IT environment constraints where Intelligent Power Manager can run for months on low-cost appliances.

Equally attractive, the Ext JS Framework offers royalty free use — a key criteria for our client library in Intelligent Power Manager. Due to our strong constraints with commercial usage and our long-term supervising role having a trusted partner was key.

After comparing several JavaScript frameworks, Ext JS appeared as the most adapted solution to our needs. Ext JS is a very well designed, lightweight framework which integrates core JavaScript language improvements, DOM manipulation helpers, and high level widgets – saving us several months worth of development. A few days were enough to acquire the basics of the API thanks to fantastic documentation. A huge thank to all Ext JS development team and the great community.

How does Ext fit into your overall technical architecture?

Intelligent Power Manager is a single page rich internet application. Ext JS is used for all client side needs from AJAX requests to data rendering. Ext JS’s State Manager allows us to keep information on selected views or on hidden, expanded and collapsed panels. Our server receives state information from Ext JS Client, keeps it in a View Manager component, stores it in a database and sends events to Ext JS Client when structure changes.


User can drag and drop devices on custom maps and view their status

How have your customers responded to your new interface?

They love it! Intelligent Power Manager is a giant leap from our previous supervision tool.


Power Source view is a custom implementation of the Ext portal example

What are some of the key Ext features that you found useful in building your product?

Viewport, grids, accordion, dialog box and state manager are key features of Ext JS that were building blocks of Intelligent Power Manager. Our software is built on a three-part interface with tree views on the left side, grids with devices or applications called nodes in the center and selected node details on right side. The State Manager keeps user interface changes in memory. The grid component quickly renders data. Lastly, the dialog boxes are used for settings and the accordion allows the user to customize information.


Dialog boxes are used to assign action settings

What features could we add to Ext to make building a rich application like Intelligent Power Manager easier in the future?

Ext custom theme generator could be a great feature for applications that need more than blue and gray colors. Intelligent Power Manager uses Ext blue theme with its own icons for its look and feel. Ext graphical resources aren’t available to custom CSS sprites and there isn’t any clear documentation on CSS rules to change.

Do you have any advice for developers just starting out with Ext for the first time?

Ext JS’s learning curve may be tough if you’re building a one page application, but samples and the forum will be the answer to all your needs. As a first step, developers could start with an HTML base with some Ext JS mixed in their code before using directly viewports and layouts.

Eaton and Intelligent Power are trademarks of Eaton Corporation.

Ext JS 3.0 – Be Outstanding

Monday, August 10th, 2009

On behalf of the Ext Team, I am pleased to announce the final release of Ext JS 3.0. This release is the culmination of tens of thousands of hours of architecture, development and community feedback. A change log and online documentation can be found on the download page. Together we’ve accomplished a level of excellence to be proud of, but we want to enable you to be even better. We want you to be Outstanding.

Excellence

My all-time favorite quote is from Aristotle: “We are what we repeatedly do. Excellence, then, is not an act, but a habit.” Sometimes, however, to reach new heights we need new habits.

As one of the world’s greatest golfers, Tiger Woods underwent an extensive swing change when he was already ranked number one in the sport. While Tiger struggled through the process, he would emerge as the greatest that ever played. Tiger would go on to a record-setting season, where he would win three consecutive majors, nine PGA Tour events, and set or tie 27 Tour records – and that was just the beginning. Tiger’s decision to modify his swing, at the top of his game, enabled him to be outstanding.

Likewise, Ext has spent the past few months modifying our “swing” so you can achieve a new level of greatness.

Assurance

For quite some time we have offered SVN access to our support subscribers. This enabled many of you to access the latest and greatest features of Ext JS for your project. At the same time, it also introduced some unforeseen bugs due to ongoing development. While this might have been exciting to some, many of us with existing projects wanted just the “fixes” without the “feature enhancements”. In order to accommodate this level of quality, we’ve had to make a significant investment in our internal processes.

Ext JS Public Commit Log

In an effort to have more transparency with our community we have created a Public Commit Log reflecting recent commits by the team. Our community can expect to have these commits in our next public release.

Our new commit template will assist us in building more interactive change logs for future releases. Component authors that extend existing Ext base components will be able to sort and locate fixes to improve their extensions stability faster using a grouping grid. In addition, the convention “ref #” and “fixes #” within the commit allow us to automate our internal QA ticket workflow.

Release Cycle

As we continue to grow our product offering, not adhering to a strict release schedule has presented issues. We want to exceed your expectations by releasing software you’ve helped support. We want to “Release Soon, Release Often.” Therefore the Ext Team will be releasing Quarterly public releases of Minor Revisions. To strengthen our support offering, we are now introducing “Monthly Patch Builds” to our support subscribers with just the “fixes”.

JS Builder 2

We have created a new build tool along with a new file format for Ext 3.0. JS Builder was a great tool for managing your Ext projects but it had a severe limitation that it could only run on Windows. We have implemented a cross-platform Java application named JS Builder 2 to replace JS Builder. In the past, you had to build multiple jsb files in order to build an entire Ext build. This process was repetitive and could become quickly time consuming if you did not write a batch script. The new jsb2 file format is able to handle multiple build targets in a single file and makes building a breeze.

Consistency

There have been some changes to the folder structure of Ext JS to be more consistent with our development going forward. Within the Ext 3.0.0 download you will now find that the “source/” directory has been renamed to “src/”. This change was made to mirror what is retrieved from the SVN repository is what you will find in a packaged download.

CSS Refactor & Improvements

Theming has now become easier than ever due to the separation of structural and visual CSS. Each component now has its own visual CSS file which describes its appearance. To create your own complete theme, simply override each rule for colors and images in the visual css directory.

Ext JS Theme

CSS Scoping

Placing standard HTML into an Ext.Panel has become easier with the new configuration preventBodyReset. When preventBodyReset is set to true your HTML will get the standard W3C suggested styling. This means that your standard HTML items will look the way you would expect them to if you loaded them up within a browser without any custom CSS.

Layout Managers

Up until now we have demonstrated only the user interface specific features which are new to Ext JS 3.0. There are also many features which aren’t UI specific. First off, both Menu and Toolbar have been updated to be proper containers with their own custom layout managers. By implementing these custom layout managers we were able to achieve the complex overflow enhancements. We have also introduced 2 new layout managers for general use named hbox and vbox. The hbox and vbox layouts enable developers to layout components horizontally and vertically based on ratios for incredibly complex interfaces.

Ext.data Improvements

The Ext.data package has been improved to incorporate Ext.data.DataWriters which can write back changes which occur client side back to the server-side. Writers are configured similarly to a DataReader and are now one of the constituent pieces of a store. The store exposes several methods like save and create which will trigger communication with the server-side based on the configuration of the writer.

// The new DataWriter component.  Elegance in simplicity.
var writer = new Ext.data.JsonWriter({
    encode: true,
    writeAllFields: false
});

Memory Management Improvements

Ext 3.0 has a new memory management model for Ext Components. This new approach eliminates many of memory consumptions issues encountered in long running Ext 1.x and 2.x applications. This new model exposes a function, mon(), which you can use to bind listeners to external objects which will be automatically cleaned up at the end of the component’s lifecycle. This new method to Ext.Component called mon(), which you can use to bind events to external objects, either DOM elements or Ext classes. mon() adds the listener to an internal collection which is destroyed when the object is destroyed, assisting you in memory management. All Ext Components have been changed to use this method where appropriate.

//Old Style
this.el.on('click', this.onClick, this);
 
//New Style
this.mon(this.el, 'click', this.onClick, this);

Innovation

In order to stay at the top of our game, we will to continue innovate. We will continue to provide elegant solutions to existing problems and lead where others have faltered. This is our promise to you and to ourselves.

Accessibility

We are 100% committed to supporting and incorporating improvements to help you build accessible applications by offering keyboard navigation, screen reader support, and a new high contrast theme for your entire application.

While Ext has always had native support for key binding for our most popular components, traversing your application using a keyboard was left up you to develop. With the new Focus Manager component, coupled with the new class inspection, you won’t need to write a line of code.

ARIA support is an evolving standard. Ext is working on adding ARIA support to the major components like Panel, Tree, Menu, Window, Grid. Using a screen reader like NVDA, with this tree example, will show how elegant accessibility can be for everyone.

Designer Preview

Constructing your interfaces in code will be a thing of the past. We are releasing a Designer Preview that will allow you to experiment with the designer interface and to explore how configs affect your layout. Soon, you will be able to build your application components using base Ext components and Certified User Extensions.

Code generation is currently not available. Our intentions are to charge a fee for this service and to enable our community to create and sell their creations on our marketplace. Our goals are lofty. We want to be the iTunes of Web App Development.


Summary

While many of the features of Ext JS 3.0 have been covered in previous posts, we’ve really just touched the surface of the power of Ext JS 3. Its been an amazing journey thus far, and we have a clear view of where we are going. We aim to provide you the tools to be Outstanding.

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.

Implementation Spotlight: Zipwhip and Ext JS

Wednesday, May 27th, 2009

With close to 100 billion text messages sent every month in the U.S., text messaging has clearly become a communication medium many of us have come to rely on. Zipwhip, a text messaging utility for the web with a polished Ext-based user interface, aims to take texting to a new level. The team at Zipwhip were eager to share their enthusiasm and approach to building with Ext.

Tell us a little bit about Zipwhip and your goals for the application.

Zipwhip is a utility for text messaging from the web. Text messaging is the fastest growing communication medium, but is still locked inside the mobile phone. We aim to help bring text messaging everywhere.

To pull off our ambitious goals, we’re using just about every web 2.0 trick in the book—comet to send carrier delivery receipts back to the browser in real-time, Ajax for server communications, download-on-demand Javascript packages (with preloading), Flash for audio notifications, and many others.

Why did you choose Ext JS?

One of the design decisions we made early on was that we wanted a rich desktop-like experience inside the web browser. We investigated the other major players in the field, but ultimately decided that ExtJS was the best. One of the things that made ExtJS especially appealing was the “Window” object that can contain “Panel” objects. Our core requirement was a windowed interface and having Ext.Window cross-browser out of the box was a major win.

Ext allowed us to focus on putting together the Ext building blocks that were engineered to be cross browser rather than reinvent the wheel. We had a limited budget and limited time and Ext provided 90% of the functionality we had in mind. We used a combination of Ext layouts and CSS to achieve many of the window structures used in the site. Theming the Ext widgets and integrating them with custom css layouts was a breeze.

Lastly, the XType architecture allowed us to create Javascript packages and download them on demand. This allows us to create an application that was not bounded by download size. A browser would have a difficult time rendering our site if not for lazy loading. What used to be a difficult custom built proposition (lazy loading) became an afterthought with Ext JS.

The Zipwhip application is extremely feature-rich. How did you manage the UI code complexity?

One of the small footnotes in Ext JS is the “plugin.” We’ve made that concept into a corner-stone of our application. Each carrier requires different functionality, features, and different business rules. With progressive enhancement we can swap in plugins that provide different implementations of various features, and ensure we only download the plugins that are needed.

Another thing that simplified my life greatly was David Davis’ (xantus) implementation of PubSub. This allowed us to decouple areas of the code, which made for a simple core framework. We implemented PubSub very late in the development process and are still working to fully take advantage of this wonderful design pattern.

Finally, keeping the code divided into standard namespaces was invaluable. We segmented common “Operating System” level widgets into Zw.controls and put all of our Ext.data.Records into Zw.data. Namespacing the code kept things manageable.

Oh yeah, and one class per file. Nobody likes a 10,000 line file.

What other techniques did you use to achieve such a gorgeous UI?

Most of what you see in the Zipwhip application is standard Ext JS with some fancy css overrides. We were surprised how easy it was to add some custom graphics to the existing Ext JS markup.

Ext.layout

One of the most awesome things about Ext JS are the Layouts! Because of the amazing flexibility we could mix css and Ext layouts to give us the desired effect with minimal code. The FormLayout (with anchoring) can manage the position of the fields, whereas CSS could manage the complexity of the Phone Preview Area.

Also Ext JS layouts improve performance and page weight. Specifically the layouts each decide when to render the content they are responsible for. This means that the Threading tab is only rendered the first time the user views the tab.

Ext.menu.Menu and Ext.DataView

Ext.menu.Menu (and the Adapter) became one of the handiest components in the ExtJS library. All of our Right-click menus were implemented with the Ext.menu.Menu. We host panels inside the menu and use a combination of CSS trickery plus Ext layouts to get the resulting effect.

Possibly the part we used the most out of Ext was the Ext.DataView. In the Contacts window (and Corkboard), we utilize an Ext.DataView and customized it to allow rendering of Ext.Components. The DataView abstracts away the difficulty of managing a list of Widgets linked to a Ext.data.Store.

Ext.SplitButton

The ContactCard (Zw.controls.ContactCard) extends the Ext.SplitButton. I know our ContactCard doesn’t look anything like a split button, but it truly was the best approach. Thanks to the way that Ext designed their components, we were able to easily modify the Template that is used to generate the button. Also thanks to the xtype, delay rendering, and plugin model the API was extremely simple.

items: {
     xtype: 'Zw.controls.ContactCard',
     closable: true,
     plugins:  [
           "Zw.controls.ContactCardEditNamePlugin",  //enable it to be editable
           new Zw.controls.ContactCardDropdownPlugin(), {  // allow right clicks on the card
           xtype: 'Zw.controls.ContactCardPopupPlugin'  //pop it up if its too small 
     }],
     listeners: {
          scope: this,
          'close': this.onContactCardClose,
          'rename': this.onContactCardRename
     }
}

What features could we add to Ext to make building a rich application like Zipwhip easier in the future?

We had the situation where we needed to create an Ext.DataView that displays Widgets. Currently you can only use an Ext.DataView with an Ext.Template. A solution that allows for controls to be created and inserted according to an Ext.data.Store would be valuable.

Additionally, “best practices” for preventing and dealing with memory leaks would make building an RIA easier. Memory leaks with templates, dataviews, and elements can cripple the development process if not planned for.

Do you have any advice for developers using Ext for the first time?

The best way to learn is to do. The rich examples provided give you a great starting point that can quite easily end up in your final product. One of the key value propositions of using Ext JS is the rich documentation – use it often.

Also put together various folder structures to gain experience for what feels right. A folder structure can make a huge difference on the quality of your product. Pick something that you can grow into and is not overly structured. I divide my OS, Controls, and Features and have found it rewarding, while not too restrictive.

Final thoughts?

Ext JS is more than an abstraction layer on top of Javascript (like other libraries). Ext JS is a set of design patterns and object models that naturally fit into application development. We continually reach inside the Ext JS treasure box when developing new functionality and find that most of the hard engineering has already been done. All that is left is to socket together a few pieces and tweak some CSS.

There is a growing amount of choice in the world of Javascript frameworks. We evaluated them all and decided Ext JS was the best. This was a hard choice for us initially, but now that we’ve launched our application to the public and it’s getting used every day, we’re really glad we selected Ext JS. The application has turned out to be everything we imagined and we like working with the Ext JS framework more and more.

Ext JS 3.0 – Remoting for Everyone

Wednesday, May 13th, 2009

As developers, we spend countless hours researching best practices to build engaging software. Often we find ourselves implementing the same repetitive functionality to wire our frontend to our backend. We’ve become accustomed to partaking in complicated design patterns to help separate logic from presentation – forcing the browser to play the role of a dumb terminal. While the RIA movement has unshackled the web browser from that awful fate, accessing our server side logic remains mostly unchanged. Ext.Direct aims to solve this issue for developers creating Ext JS applications by providing a single communication point with the server-side.

Common Concerns

At Ext, we’ve integrated Ext JS with many languages and platforms from Mainframe systems to Java to MUMPS to Perl. However, we noticed that there are several issues that are common across all server-side languages when creating Ext apps.

  • How to organize code and where to place appropriate business logic.
  • Parsing and formatting data on the server side.
  • Keeping a maintainable structure.
  • Parsing Ajax responses and retrieving error conditions.
  • Doing data validation in multiple areas.

Wouldn’t it be nice if we had a cross-language standard that solved the problem the same way?

Introducing Ext.Direct

Ext.Direct is a new package in Ext JS 3.0 that helps alleviate many of these issues by streamlining communication between your client and server. When using Ext.Direct, you can expect to write 30% less code by eliminating common boiler plate code.

The Ext.direct namespace introduces several new classes for a close integration with the server-side. New classes have also been added to the Ext.data namespace for working with Ext.data.Stores which are backed by data from an Ext.Direct method.

Ext.Direct uses a provider architecture, where one or more providers are used to transport data to and from the server. There are several providers that exist in the core at the moment, for example a JsonProvider for simple JSON operations and a PollingProvider for repeated requests. One of the most powerful providers is the RemotingProvider.

RemotingProvider – Client-side Stubs

The RemotingProvider empowers the developer by mirroring server side methods on the client-side and allowing them to call the server-side methods as if they were sitting on the client-side. The server-side simply describes what classes and methods are available on the client-side. This allows for code to be organized in a fashion that is maintainable, while providing a clear path between client and server, something that is not always apparent when using URLs.

Intrinsic Call Batching

The provider immediately batches together calls which are received within a configurable time frame and sends them off in a single request. This assists in optimizing the application by reducing the amount of round trips that have to be made to the server machine. If a series of calls are received within the specified timeout period, the calls will be concatenated together and sent off to the server as a single request.

Server-side Stacks

In order for Ext JS’s Direct protocol to work you must have a compatible Ext.Direct Server-side stack residing on your server. The server-side stacks use a ‘router’ to direct requests from the client to the appropriate server-side method. Because the API is completely platform-agnostic, you could completely swap out a Java based server solution and replace it with one that uses C# without changing your JavaScript at all. Ext is providing a complete remoting specification along with several reference implementations of different server-side stacks in PHP, .Net, Ruby and ColdFusion. Each of these are licensed under an MIT license, so that the community can expand upon what has already been done and integrate them into their favorite MVC framework such as Zend Framework or Struts.

An Example – The Ext Support App

Support subscribers have a new tool at their disposal to receive a response from the Ext Team. We have developed the new Ext-based application that will be used to streamline the process of managing user support queries . The Ext support application is built on top of Ext 3.0 and utilizes Ext.Direct extensively.

Dashboard

In order to see the benefit of Ext.Direct more clearly, let’s take a look at how we utilized it. Ext.Direct enables server-side developers to easily expose methods from the server-side to the client-side. In this example, we are exposing two methods of the TicketAction class – getTickets and getOpenTickets.


We can now call these methods as if they were local client-side methods without worrying about how the request is sent to the server-side and how the response is processed. We can also use these methods to populate an Ext.data.Store with the new DirectStore object.

var store = new Ext.data.DirectStore({
    storeId:'open-tickets',
    directFn: TicketAction.getOpenTickets,  //it's really that easy
    paramsAsHash: true,
    idProperty: 'tid',
    fields: [{
        name: 'tid',
        type: 'int'
    },
        'title',
        'display_name',
    {
        name: 'last_post_time',
        type: 'date',
        dateFormat: 'timestamp'
    }],
    sortInfo: {
        field: 'last_post_time',
        direction: 'DESC'
    }
});

The TicketAction.getOpenTickets method can be called at any time, it is not required that it used solely in conjunction with a store.

I hope that the simple example above illustrates how Ext.Direct can help you better organize your client-server communication in your applications.

Ext.Direct Forum

We have added a new Ext.Direct forum under the the Ext JS 3.0 category. Several community members have already submitted server-side stacks for their favorite environments. We already have a list of several server-side stacks in a stickied forum thread. We encourage the community to contribute back server-side stacks for their favorite environments by implementing the Ext.Direct Remoting specification. There are five sever-side stacks currently available in our Ext.Direct pack with more implementations soon to come.

Ext JS 3.0 RC1.1 Released

Monday, May 4th, 2009

The Ext Team is proud to announce the release of Ext JS 3.0 RC1.1 available for immediate download. This new version of the Ext framework is the culmination of many long hours of dedication by the Ext Team. We appreciate our community of testers and supporters whom have made the stability of this release possible. Ext 3.0 is another leap forward providing increased performance, consistency, flexibility and UI enhancements to make you more productive – all without a significant size increase.

There are many enhancements in Ext JS 3.0, too many to include in a single post. Some of the major features in Ext JS 3.0 are the splitting of Ext Core and Ext JS, Charting for visualizations, additional User Interface improvements, CRUD-like support with Ext.data.DataWriter, Remoting using Ext.Direct, CSS enhancements to make theming easier, and Accessibility improvements – Section 508 and ARIA support. We also fixed several browser issues for the latest Chrome and Safari releases and added IE8 support.

Ext Core

The first major change to Ext JS 3.0 is that it has been built to sit on top of the new Ext Core library. Ext Core 3.0 is a lightweight library intended to give you all of the functionality to enhance a webpage. Drawing upon our experience creating rich user interfaces, we isolated the most powerful features used to enhance new or existing web pages and placed them in the Ext Core library. Developers familiar with Ext JS can leverage their existing skillset to provide an enhanced user experience to their web pages.

Winston Churchill once said, “We make a living by what we get, but we make a life by what we give.” As avid members of the open source community, Ext continues to give back. This time, we are releasing Ext Core under the permissive MIT license to be used by all for free.

New UI Features

Ext JS 3.0 introduces several new UI Components and enhancements to many existing components.

New UI Components

  • RowEditor
  • ListView
  • Charting
  • ButtonGroup
  • GroupTabs

RowEditor

Editing a row in a grid just got a lot easier. The RowEditor is another great new UI component allowing you to rapidly edit full rows in a grid. You can even enable a validation mode which uses the new AnchorTips to notify the user of all validation errors at once.

Row Editor

ListView

ListView is a high performance, light-weight version of a grid like display. It provides you with selection, column resizing, sorting and other DataView features. The columns have percentage based widths and uses templates to render the data in any required format. If you need to show your data in a grid like display without some of the more advanced features of the Grid, then ListView is the perfect solution.

ListView

Charting

The Ext.chart package will allow you to visualize your data with flash based charting. Each chart binds directly to an Ext.data.Store. The new FlashComponent class, which extends BoxComponent, allows you to easily create custom Flash Components. By binding the charts directly to an Ext.data.Store, you don’t have to worry about updating your chart, they will update automatically.

Charting

ButtonGroup

ButtonGroup is a new component that will allow you to group together Buttons of different sizes to create complex toolbars enabling your users to find the most common actions first. We created an example to show the flexibility and power of this new component.

Button Group

Sample code:

var btnGroup = new Ext.ButtonGroup({
       title: 'Clipboard',
       columns: 2,
       defaults: {
            scale: 'small'
       },
       items: [{
           xtype:'splitbutton',
           text: 'Add',
           iconCls: 'add16',
           menu: [{text: 'Add'}]
        },{
           xtype:'splitbutton',
           text: 'Cut',
           iconCls: 'add16',
           menu: [{text: 'Cut'}]
        },{
           text: 'Copy',
           iconCls: 'add16'
        },{
           text: 'Paste',
           iconCls: 'add16',
           menu: [{text: 'Paste'}]
        }]
     });

Grouped Tabs

Horizontal navigation can always be challenging. We’ve introduced GroupTabs to assist you in creating portal layouts similar to iGoogle, or providing a interface to access similar tasks quickly.

Group Tabs

Sample Code:

var viewport = new Ext.Viewport({
        layout:'fit',
        items:[{
            xtype: 'grouptabpanel',
            tabWidth: 130,
            activeGroup: 0,
            items: [{
                mainItem: 1,
                items: [{
                    title: 'Tickets',
                    iconCls: 'x-icon-subscriptions',
                    tabTip: 'Tickets tabtip',
                    style: 'padding: 10px;',
                    html: Ext.example.shortBogusMarkup         
                }, {
                    title: 'Subscriptions',
                    iconCls: 'x-icon-subscriptions',
                    tabTip: 'Subscriptions tabtip',
                    style: 'padding: 10px;',
                    html: Ext.example.shortBogusMarkup         
                }, {
                    title: 'Users',
                    iconCls: 'x-icon-users',
                    tabTip: 'Users tabtip',
                    style: 'padding: 10px;',
                    html: Ext.example.shortBogusMarkup         
                }]
            }, {
                expanded: true,
                items: [{
                    title: 'Configuration',
                    iconCls: 'x-icon-configuration',
                    tabTip: 'Configuration tabtip',
                    style: 'padding: 10px;',
                    html: Ext.example.shortBogusMarkup
                }, {
                    title: 'Email Templates',
                    iconCls: 'x-icon-templates',
                    tabTip: 'Templates tabtip',
                    style: 'padding: 10px;',
                    html: Ext.example.shortBogusMarkup
                }]
            }]
        }]
    });

Enhanced Components

  • Buttons
  • Toolbar Overflow
  • Menu Overflow
  • AnchorTips
  • Buffered GridView
  • Debug Console

Buttons

Buttons in Ext JS 3.0 have been refactored to be a valid BoxComponent which enables them to partake in layout management. Buttons can now scale to any height or width and have advanced text positioning. Rather than being limited to only positioning buttons in a buttons configuration, they can now be placed anywhere you please.

Toolbar overflow

Toolbar Overflow

Toolbars can now create a menu for items that don’t fit the visible toolbar area. The items in the menu still react with the same handlers as the toolbar items. This new behavior is turned on by default and can be disabled with the configuration option enableOverflow.

Toolbar overflow

Menu Overflow

Menus now also handle overflowing in a more gracious manner. Whenever a menu gets so long that the items won’t fit the viewable area, it provides the user with an easy UI to scroll the menu. This feature is turned on by default and can be disabled by the configuration option enableScrolling.

Menu overflow

ToolTips

Tooltips now support an anchor configuration which will allow you to bring attention to a particular element or component with a small callout arrow.

ToolTip

Sample Code

new Ext.ToolTip({
        target: 'bottomCallout',
        anchor: 'top',
        anchorOffset: 85,
        html: 'This tip\'s anchor is centered'
    });

Buffered GridView

Buffered GridView enhances performance by waiting to render rows until they are visible. As your user scrolls away from content which is no longer visible, the BufferedGridView will clean up the old DOM markup to minimize the DOM structure. As a result of the smaller markup, the performance of resizing, forceFit, autoExpandColumn and other layout and DOM manipulation features in a large grid will improve substantially. Buffer GridView is limited to working on fixed height rows.

Buffer Grid

Debug Console

The Debug Console has been revamped and we have added three new tabs, named Component Inspector, Object Inspector and Data Stores which are specific to Ext Development. Component Inspector will show you all components which have currently been registered with the ComponentMgr and the parent child relationships among components. By mousing over a particular component you will notice that it will be masked on the page. The Object Inspector will allow you to inspect objects and peek inside their contents. The Data Stores tab will display all stores which have been registered with the StoreMgr and how many records are currently loaded into each store.

Debug Console

Summary

This blog entry covers all of the user interface components which are new to Ext JS 3.0 or which have been significantly enhanced. Stay tuned for another entry that details all of the non-ui related improvements such as Ext.data package enhancements, Accessibility improvements, Ext.Direct and the refining of memory management within Ext.

Upgrading

User upgrading to Ext JS 3.0, will be happy to hear there are little to no breaking changes. We took great care in only creating breaking changes where absolutely necessary. You may encounter issues upgrading if you were previously manipulating private properties of an Ext.menu.Menu or an Ext.Toolbar or if you had custom styling of an Ext.Button.

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.

Ext Core 3.0 Beta Released

Saturday, April 4th, 2009

As we approach the three year anniversary of the initial release of Ext, the Ext Team is proud to announce the immediate availability of Ext Core 3.0 beta for download. Ext Core provides a cross-browser consistent API for performing the most common tasks in JavaScript development for web pages. Ext Core is released under a permissive MIT license - there is no cost to use Ext Core - it's free for everyone.

Drawing upon our experience creating rich user interfaces, we isolated the most powerful features used to enhance new or existing web pages. Ext Core is a subset of the upcoming Ext JS 3.0 release optimized for speed & file size. Developers familiar with Ext JS can leverage their existing skillset to provide an enhanced user experience to their web pages.

Ext Core Overview

Ext Core distinguishes itself from other JavaScript libraries with a well defined object-oriented structure, which enables you to write clean and reusable code. Ext Core provides cross-browser abstractions for:

  • DOM manipulation and traversal
  • CSS management
  • Event handling
  • Dimensions and Sizing
  • AJAX and JSON Support
  • Animations

In addition to providing cross-browser abstractions for the DOM, Ext Core also includes some of the most used and popular utilities from Ext JS.

  • Classical Inheritance Class System
  • Observable Class
  • Markup generation and Templating
  • Timed code execution
  • URL encoding and decoding

Library Size

Ext Core is perfect for inclusion in a dynamic web page or even a small application. We have gone through the source with a round of refactoring aiming to get the best possible compression. Considering all of the included features, Ext Core has a small footprint. It has a compressed footprint of 25K minified and gzipped.

Ext Core Manual

Another aspect of Ext Core that sets itself apart from other libraries is the Ext Core Manual. Written by the authors of Ext Core, and reviewed by the community Ext-perts, the Ext Core Manual provides beginners and experienced developers an in-depth view of how to use every aspect in Ext Core. No method or class has gone uncovered. This mini-book (75 pages printed) is meant to amplify the existing API documentation. We encourage everyone, including Ext JS users, to read the manual to sharpen their understanding of Javascript and Ext.

Examples using Ext Core

To illustrate the capabilities of Ext Core, our team built a few of the most commonly used extensions on the web. They also serve as a great reference for creating your own extensions. Given the small footprint of Ext Core, we were able to embed the Example Explorer into this blog post. These examples are freely available to be used on your web pages today.

DomQuery and CompositeElementLite

DomQuery provides high performance selector-based element retrieval. It supports most of the CSS3 selectors specification, along with a few custom selectors and basic XPath. A common use case when working with the DOM is manipulating a collection of elements. Using an instance of CompositeElementLite, Ext Core allows you to interact with a collection the same way you would with a single element. Here is an example of adding a class to a collection of elements.

// selects a collection of elements and adds the class 'myCls' to each one.				
Ext.select('div:has(> span.someClass)').addClass('myCls');

Event handling made easy

Ext Core's event handling abstraction gives you cross-browser normalized event handling and support for custom events. On top of that, it provides configuration options for delaying, buffering, delegating, and targeting events. In the example below, we update an element to indicate a click event occurred.

Ext.fly('elId').on('click', function(e, t){
    // e is normalized cross browser event object
    // t is the target element
 
    // Update contents of the element with id "log" to notify the user of the event firing
    Ext.fly('log').update('You clicked on the element with id: ' + t.id);    
});

Ajax Requests

Ext Core has a clean cross-browser abstraction for making XMLHttpRequests. It gives you an intuitive API to retrieve and send data to the server without requiring the page to refresh. Take a look at a basic Ajax request using Ext Core :

Ext.Ajax.request({
    url: 'serverSide.php',
    success : function(r){
        // using the built-in Ext JSON support
        var data = Ext.decode(r.responseText);
 
        // data is now a regular Javascript object
        console.log(data.items[0].title);
    }
});

If all you need to do though is update the contents of an element you can use the shorthand version:

Ext.fly('elId').load({
    url: 'serverSide.php'
});

Some final words

With 70,000+ registered members on the Community Forums and a company dedicated to making Ext "a foundation you can build on", we hope that this core library will find its way into many of your dynamic web pages and make your lives as web developers easier and more enjoyable. We had a lot of fun putting Ext Core and the examples together, and we are looking forward to seeing the great things you will create using it.

Ext JS Books: Resources to Master the Framework

Monday, March 16th, 2009

Learning an exciting software framework can be like climbing a mountain — it requires proper equipment, technical skill and determination. To help new and seasoned community members reach the Ext apex, three new books have come to our attention to help guide your path.

Ext JS in Action

Author: Jesus D. Garcia, Jr.
Discount code: extjs40 (40% off until April 1)
Description:

  • Part 1: Introduction to Ext JS

    • A framework apart
    • An Ext JS primer
    • A place for components
    • Organizing components
  • Part 2: Ext components

    • Building a dynamic form
    • The venerable Ext DataGrid
    • Taking root with Ext Trees
    • Toolbars and menus
    • Advanced element management
    • The Ext toolbox
    • Drag and drop
  • Part 3: Building a configurable composite component

    • Developing object-oriented code with Ext
    • Building a composite widget
    • Applying advance UI techniques
 

The author, Jesus Garcia is a long-time member of the Ext JS forums with a well-documented history of helping others in his 7600+ posts. His book, Ext JS In Action, will make an excellent companion to Learning Ext JS below, since it takes a more-technical bottom-up approach to teaching Ext JS. Mr. Garcia demonstrates a thorough understanding of low-level DOM manipulation and event-handling, including the differences between browsers.

The book contains many excellent diagrams primed for printing and posting beside your monitor, like the following illustration of the Ext class hierarchy depicting common descendants of Ext.Component:

Though still in development, this excellent book for both Ext JS new-comers and advanced developers alike is currently available through the Manning Early Access Program. Says Steven Hong of Manning Publications:

“I’ve taken the liberty of setting up a discount code for your readers to use. Until April 1, your readers can use the code extjs40 at www.manning/garcia to receive 40% off the retail value of the book”

We at Ext JS thank Manning Publications for their generous offer to our community.

Learning Ext JS

Authors: Shea Frederick, Colin Ramsay, Steve “Cutter” Blades
Description:

  • Chapter 1 introduces you to the process of installing the required Ext JS library files, and setting up a basic page that displays an alert-style message. This provides us with a way to test whether your setup was done correctly, and whether you’re ready to play with some code. We also cover how to set up other base libraries such as jQuery, YUI, and Prototype, to work in conjunction with Ext JS.
  • Chapter 2 covers how to interact with the web page and the user. With example code that uses simple components, we quickly start to see the level of user interactivity that Ext JS provides right out of the box. We assemble a series of dialogs that appear and modify the existing pages depending upon the users’ inputs.
  • Chapter 3 launches us into using the first major widget—forms. We start by creating a simple form with three fields, explore the different form field types, and then add some simple validation to our form. From there we move on to creating custom validation and database-driven combo-box’es and handling form submissions.
  • Chapter 4 provides an overview of how to use toolbars and buttons within your
    application. These components are typically undervalued, yet they provide crucial user interface functions. We jump straight into creating toolbars with buttons, split buttons, and menus, along with adding mechanical elements such as spacers and dividers. Next, we cover customizing the toolbar with stylized icon buttons and form fields.
  • Chapter 5 covers grids—the most widely-utilized component in the Ext JS library. In
    this chapter, we learn how to set up a grid panel using both local and remote data, and in both in XML and JSON formats. We also discuss how to prepare different data types and how to create renderers that will style and format the data to your preference. Using the selection model and paging are among the many interesting points covered in this chapter.
  • Chapter 6 dives into editor grids. Here, we learn how to set up an editor grid using
    different form field types, and how to save changes made in the grid back to the server or database. We also discuss tactics for adding and removing rows of data to and from our data store, and the server or the database.
  • Chapter 7 explores the concept of using the layout component to bring all the portions of your application together into a cohesive web application. We start by using a viewport with a border layout to contain the many parts of our application. From there we are able to add other layout features such as tab panels, accordions, and toolbars. We finish up by learning how to nest layouts and make dynamic changes to the layout components.
  • Chapter 8 discusses the presentation of hierarchical information using the Ext JS Tree support. Using real-world examples of hierarchical data, you will discover how to display and manipulate a Tree view. You will use AJAX techniques to persist the modifications to a server and learn how to tweak the Tree to support advanced scenarios.
  • Chapter 9 demonstrates how Ext JS can provide attractive user prompts that can either present information or accept input. We then discuss the extension of these dialogs in the form of Ext.Window, a fully-fledged means of creating customizable pop-up windows.
  • In Chapter 10, we take a tour of the visual effects available in the Ext JS effects package. You will learn how to apply animations to create smooth transitions and notifications to enhance the user experience.

Well-written and well-produced, Learning Ext JS takes a top-down approach to teaching Ext JS, which might be daunting for those having little experience with the framework. However, this approach will certainly immerse the reader quickly into a number of common techniques used througout the framework, like XType, configuration objects and common component configuration parameters.

After starting off with an excellent tutorial on how to download and include the library’s JavaScript & CSS assets, the authors quickly jump full-force into Ext forms in the third chapter and beyond, guiding the reader through all of Ext’s high-level widgets using an evolving “movie database” application with server-side code examples in both php and ColdFusion.

var store = new Ext.data.GroupingStore({
  url: 'movies.json',
  sortInfo: {
    field: 'genre',
    direction: "ASC"
  },
  groupField: 'genre',
  reader: new Ext.data.JsonReader({
    root:'rows',
    id:'id'
  },fields);
});
var grid = new Ext.grid.GridPanel({
  renderTo: document.body,
  frame:true,
  title: 'Movie Database',
  height:400,
  width:520,
  store: store,
  autoExpandColumn: 'title',
  columns: // column model goes here //,
  view: new Ext.grid.GroupingView()
});

In the final chapters, the authors provide some excellent material in more advanced topics, like Ext’s data package, extending Ext core classes with Ext.extend, overriding initComponent & onRender in your extensions and organizing your extensions into separate files. If you’ve already got experience with Ext JS, this book will do wonders in focusing your skills.

Ext JS Projects with Gears

Author: Frank Zammetti
Description (by the author)

  • Nine chapters in total. The first is your typical “Intro to Ext JS”. I start with a few pages on RIA development, the evolution of web development in general, etc. I talk about how there’s now a lot of choices in toolkits and of course wind up saying that Ext JS is the best of the bunch IMO I then quickly give a basic first application with Ext JS (not much more than “hello world”). Then it’s on to some details… things like Ext JS’s overall structure, the stuff added to intrinsic classes, then the stuff directly under the Ext namespace. I then go into Ext.util in some detail.
  • Chapter 2 is the more “advanced” introduction… I cover the inheritance model behind widgets, the basics of widget usage, then details about the concept of layout and layout managers. I then cover most of the core widgets in some detail. I also talk about the data subsystem, templating capabilities, drag-and-drop and state management. I also talk a bit about plugins and some of the great user extensions out there. This chapter also introduces Gears, which although the lesser player in the game is a big part of the chapters to follow.
  • Chapters 3-9 are the project chapters. Each of them presents a sovereign, unique, full application using Ext and Gears. This is the meat of the book (the “practical projects”!). The idea here is learn-by-example (and learn-by-doing because the end of each chapter presents suggested exercises to the reader).

In the Ext forum-post where Mr. Zammetti introduced Ext JS With Gears, he stresses:

“Keep in mind that this book is in no way trying to be an exhaustive, detailed look at Ext… I purposely had to leave some things out, but I tried my best to cover everything that I thought was most relevant to a modern web developer.”

Frank Zammetti is a prolific writer at Apress, authoring five books there so far.

extjs.com

Those new to Ext JS should be aware of a number of important learning resources within extjs.com itself, where you’ll find material on gathering the right equipment along with lessons and tutorials to hone your skills, from javascript fundamentals to advanced techniques.

Ext API Docs

Like a GPS device for the framework, the Ext API docs have always been of superior quality and are one of the primary reasons for the framework’s success. You can find the docs online at www.extjs.com/docs, in your locally downloaded copy of the library (eg: ext-2.2/docs) or as a downloadable Adobe Air application.

The Ext Forum

The Ext forum is a large, diverse community with over 60000 registered users (as much as a small city!), including a couple of heavy-weights with over 10000 posts. These guys have seen it all and know what they’re talking about (some of you might recognize the fourth character in this list, Mr. jgarcia, the author of Ext JS In Action above.)

Ext Forum Top-posts

Whichever server-side technologies you’re bringing with you into Ext — .NET, Merb/Ruby on Rails, Enterprise Java or PHP — you’ll find someone in the Ext Forum who’s already been down the road you’re on, so register now—it’s free.

Community Learning Center

Available through the Support tab on our website, the Ext JS Community Learning Center contains an evolving collection of training resources that will accelerate your transition into Ext JS, including the Ext JS Manual, interactive demos, tutorials, screencasts and much more.

Ext Enterprise Training

We love to travel and we love to teach. From beginning JavaScript and CSS to custom component creation and CSS theming, your team will learn how to bring it all together to create innovative web user interfaces with hands-on training by a member of the Ext Core Development Team.

Ext Conference 2009

This April, join Jack and the Core Development Team as we host the 1st Annual Ext Conference and Ext 3.0 release party. Connect with other members of the Ext Community while learning about Ext 3.0′ s innovative features.

Tools, Black-belts & Meetups

Jozef Sakalos, aka Saki

One of the top Ext experts in the world as well as a member of the Ext Support Team, Saki maintains an Ext Examples Page as well as an active blog.

The User-extension Repository

The User-extension repository is an attempt to build a centralized database of API documentation for 3rd-party ux (user-extension) components using the familiar Ext documentation style.

Meetups

Both Jay Garcia (author of Ext JS in Action) and Shea Frederick (an author of Learning Ext JS) have been partaking in the Baltimore/DC JavaScript UserGroup Peter Kellner has also organized the San Francisco Ext JS UserGroup. I gave a presentation last week on the innovative new features Ext 3.0 to the S.F. Ext JS Group via a webex.

Conclusion

As the Ext framework matures, the community surrounding it is also maturing. We are seeing several excellent resources for learning Ext sprout up and an extremely active blogosphere discussing how people are using Ext in their day to day projects. If you regularly blog about Ext, please let us know we’d like to follow you on your journey in discovering Ext. Start a meetup in your area and connect with other members of the community who are building applications on the Ext platform.

Pixel Bender Explorer: Bending Ext AIR Apps

Thursday, February 19th, 2009

Pixel Bender is an exciting new technology by Adobe which brings video and image processing capabilities to the flash runtime. It allows you to create and apply filters to ‘bend’ pixels and create compelling animations which have never been possible in an HTML environment. Because Adobe AIR uses flash to load any HTML content, we can leverage these powerful filters on a standard Ext Application in the AIR environment. Ext is releasing a Pixel Bender Explorer demo which allows you to explore many of the new filters which have been created by the Adobe Community and demonstrates how to integrate them into an Ext Application.

Pixel Bender Explorer

Download and install the Pixel Bender Explorer application. Apply different filters or effects by clicking on the Filter Grid. By selecting a filter on the left hand side and adjusting the sliders you can apply live effects to the current target video, browser or image. For some of the filters we have already built some animations such as bend/flatten, waveIn/waveOut, dissolve/combine and tileIn. You can test these animations by choosing the appropriate filter and clicking the animation button above the sliders. These animations can be called within code with a single method call on any Ext.air.NativeWindow or Ext.air.VideoPanel.

Here is an example of bending an Ext.air.NativeWindow:

var win = new Ext.air.NativeWindow({
        file: '../html/browser.html',
        transparent: true,
        chrome: 'none',
        width: 640,
        height: 480
});
win.bend();

Construction of Pixel Bender Kernels

Constructing your own Pixel Bender Effects and Animations is a multi-step process. First, you need to construct a kernel or filter for Pixel Bender. These kernels describe how each pixel should change for a single frame based on a mathematical algorithm which you implement. Kernels are written in the Pixel Bender Kernel Language and compiled with the Pixel Bender Toolkit. The language is very similar to GLSL (OpenGL Shading Language). The benefits of writing effects in this language is that are generalized to all common video hardware platforms and can be used in other Adobe Applications such as After Effects and Photoshop. There is a great article, Pixel Bender basics for Flex and AIR, created by Charles Ward of Adobe that explains the Pixel Bender language in more detail.

Using Pixel Bender Kernels

After compiling the source code of a Pixel Bender Kernel the extension will change from .pbk (source form) to .pbj (binary/compiled form). The compiled version of the kernel is the only file that we need in order to use the new kernel. After compiling a new kernel, you can place the .pbj file in the kernels/ directory of your installed Pixel Bender Explorer application and you will be able to test it after restarting the application.

Pixel Bender Exchange

There is a lively community around the creation of Pixel Bender Kernels on the Pixel Bender Exchange, most of which are under an open source license. Download a few kernels and take them for a test run in the Pixel Bender Explorer. The Explorer provides you all of the tools necessary to create your own custom animations for a NativeWindow or VideoPanel. For example, if you wanted to use the smudge filter created by Frank Reitberger as an animation you would determine the paramName that you want to change and where it should start and end along with a few other configurations.

Using the same NativeWindow as shown in the example above:

win.animFn({
    paramName: 'amount',
    reset: true,			
    startValue: 5,
    endValue: 0,
    duration: 1,
    mode: 'easeOutStrong',
    url: 'app:/kernels/smudger.pbj'
});

Code Explanation

This will tween the value of the parameter amount from 5 to 0 over a duration of 1 second. This will have an effect which I would name smudgeIn because it starts smudged and will eventually look normal. We are using one of Robert Penner’s Easing equations easeOutStrong. Support has been added for 15 different easing modes, which explain how fast/slow we adjust the value over time. The filter also needs to be reset at the end of animation because there is no parameter which will not affect the image. (When we set amount to 0 it will still have a slight change on the target.) Anyone who is developing Pixel Bender Kernels, I encourage you to have a parameter which reflects the unapplied state vs the fully applied state. A good example of this are the Adobe filters which use the parameter of transition (0 does nothing, 1 is fully applied).

Wrapping up

Pixel Bender can spruce up an Ext.air application by adding custom animations to wow your users. However, you should be cautious about the over-use of these filters throughout your application. For a good example of how effective these filters and animations can be to provide proper user feedback you should check Adobe’s signature sample BlackBookSafe. Each time an animation occurs it is clear why it happened, not a surprise to the user and adds character to the application. When using these animations you should strive for the same goal, not to surprise your user, but to impress them.

Ext Conference 2009

Tuesday, February 3rd, 2009

Ext is pleased to announce the 1st Annual Ext Conference and Ext 3.0 release party on April 14 to 16, 2009 in Orlando, Florida! Join Jack and the Core Development Team for an intense 3-day conference exploring all of the new features packed into Ext 3.0. Discover best practices for building applications and connect with other members of the Ext Community. The conference provides sessions which will be of interest to all parties involved in application development including managers, designers, developers and application architects.

Conference Agenda

Sessions cover all three libraries included under the Ext umbrella: Ext JS 3.0, Ext GWT 2.0 & Ext Core 3.0.

  • Ext JS is the library that allows you to write your applications in JavaScript with complete platform independence.
  • Ext GWT is the library that allows you to leverage pre-existing Java skills for client-side development.
  • Ext Core is a lightweight, feature rich library weighing in at 49.9Kb and is perfect for your smaller projects and websites.

Connect with the Ext Team and guest speakers as they share their expertise in application development with 24 exciting sessions.

Conference Location

The Ext Conference is being held at the Ritz-Carlton Grande Lakes conveniently located only 10 miles away from Orlando International Airport and Disney World. Bring the family and stay for the weekend. The Ritz-Carlton provides complimentary transportation to local theme parks like Universal Studios and SeaWorld. By working directly with the Ritz-Carlton, we were able to negotiate a special rate for all Ext conference attendees. You can stay at the Ritz-Calrton’s luxurious 5-star hotel for $149 a night.

Connecting the Community

Ext recognizes that our community is critical to the success of our projects. Without the rich ecosystem of forums, user extensions, themes and samples that the community has provided we would not be in the same place we are today. The conference provides a unique opportunity for the community to come together and share innovative ideas about Ext development. Check out the live attendance list which lets you know who has already registered for the conference.

Register Today

Ext will be offering a $250 early registration discount for registering by February 10th 12:00 AM EST. Community leaders will also be recognized during the registration process. Register today and connect with the Ext Team and Community in April!

Join Ext in Helping Toys for Tots

Thursday, December 18th, 2008

This past week members of the Ext team were sitting in my living room watching TV when we saw CNN report Toys for Tots is in desperate need of toys this year. Toys for Tots is a non-profit organization that works with the United States Marine Corps to gather and distribute toys to needy and underprivileged children during the holidays. Toys for Tots needs double the amount of toys this year. Because of the challenging economic times, many families are unable to afford gifts for their children and not as many people are able to donate. That’s when we began thinking “How can we help?”

Origin of Toys for Tots

“Toys for Tots began in 1947 when Major Bill Hendricks, USMCR and a group of Marine Reservists in Los Angeles collected and distributed 5,000 toys to needy children. The idea came from Bill’s wife, Diane. In the fall of 1947, Diane handcrafted a Raggedy Ann doll and asked Bill to deliver the doll to an organization, which would give it to a needy child at Christmas. When Bill determined that no agency existed, Diane told Bill that he should start one. He did. The 1947 pilot project was so successful that the Marine Corps adopted Toys for Tots in 1948 and expanded it into a nationwide campaign.” from http://www.toysfortots.org

How You Can Help

Toys for Tots accepts several different types of contributions, including donations, and toys at drop offs through out the nation. Locate a drop off location near you.

How Ext will Contribute

Ext will donate a portion of sales to the charity, Toys for Tots for the entire month of December. If you already made a purchase during December, you have already contributed. Ext will donate a portion of each sale from our store for both Ext JS & Ext GWT. We will donate the following amounts for the purchase of a license.

  • Single Developer: $20
  • Team: $50
  • Workgroup: $150
  • Enterprise: $500

A Call to Action

We understand that there are many causes that members of our community feel just as strongly about, and we encourage you to join us or to support the cause of your choosing in any way that you can. Happy Holidays from Ext!

ExtPlayer – An MP3 Player developed with Adobe AIR and Ext JS

Monday, November 24th, 2008

In collaboration with Adobe, Ext is releasing several new enhancements to the Ext.air package today. These include improvements to existing classes responsible for Sounds, Windowing and Database as well as new classes responsible for Notification, Clipboard and File System Interaction. We have also added new samples demonstrating how to use these new features. One of these examples is ExtPlayer, a simple MP3 player, that leverages the new Ext.air.MusicPlayer and Ext.air.Notify classes. You can install ExtPlayer or download the source.

Ext.air.MusicPlayer

Ext 2.0.2 introduced an Ext.air.Sound class, which is useful for playing small sounds such as beep and chimes. In contrast, Ext.air.MusicPlayer is meant for long running sounds such as music and podcasts which you would never want multiple files playing at the same time. MusicPlayer supports all of the basic operations, stop, pause, play and skipTo along with supporting events. The MusicPlayer enables the developer to add music and podcasts to their AIR-enabled Ext application very quickly.

var mp = new Ext.air.MusicPlayer();
mp.adjustVolume(0.5);
mp.play(url);


Ext.air.Notify

The new notification class allows you to notify your users with toast or growl-like messages from the operating system. This allows you to notify users that something important has occurred even when your application may not be visible. By displaying these notifications at the operating system level it is sure to get the users attention without being lost within a browser tab.

var msg = 'Title: {0}<br/>Artist: {1}';
var sample = new Ext.air.Notify({
    msg: String.format(msg, id3info.songName, id3info.artist),
    icon: '../famfamfam/music.png'
});



Window and App API’s

We added methods and configurations for common window manipulation tasks that did not already exist. Ext.air.NativeWindow now exposes methods to re-order windows, set a window as always on top, and enable full-screen mode. A new singleton, Ext.air.App will allow you to set your application to start on login and get the currently active window.

Ext.air.App.launchOnStartup(true);


Ext.air.Clipboard

Ext.air.Clipboard allows you to interact with the system’s clipboard. Developers can determine if a particular format has data, set the data and get the data. At this point, this is largely a pass through to an existing class from Adobe air.Clipboard.generalClipboard. There may be enhancements or workarounds which Ext will add in the future including integration with drag and drop.

Ext.air.Clipboard.setData('air:text', 'Sample set on the clipboard');
var data = Ext.air.Clipboard.getData('air:text');


Ext.air.VideoPanel

Ext.air.VideoPanel enables you to embed flash based video while maintaining the same functionality as an Ext.Panel. VideoPanel’s can also take part in Ext layout management. This means that you can nest your Video’s in a border layout, add toolbars, buttons, just as you have become accustomed to. You just need to provide the recorded FLV to playback or provide a camera connected to the PC. You can even watch the video fullscreen in high definittion!

var vp = new Ext.Viewport({
    layout: 'fit',
    items: [{
        id: 'video',
        xtype: 'videopanel'
    }]
});
Ext.getCmp('video').loadVideo('sample.flv');


AIR and the Future of Ext.air

Adobe AIR enables web developers to use their existing skill set to create desktop applications. AIR is a cross-platform runtime and allows you to develop your application once and then deploy it across Windows, OS X or Linux. (The Linux runtime is currently in beta status.) Adobe AIR 1.5 was shipped last week at AdobeMAX and features database encryption, an updated WebKit (including SquirrelFish) and Flash Player 10.

Ext will be adding support to encrypt your SQLite database soon. Another exciting technology which was included in Adobe AIR 1.5 is PixelBender. PixelBender allows you to apply lightning fast image or video processing filters to your application. An important feature of AIR that many people are unaware of is that you can mix and match Ajax, Flash and Flex technologies. There are several existing open-source ActionScript libraries which you can take advantage of immediately by including them in your application. A new project from Adobe named Alchemy will even let you compile C or C++ code to the ActionScript virtual machine. You could then consume this code in an Ext application on the AIR platform! The entire Ext.air package including samples can be downloaded here. It will also be included in the next release of Ext.

Ext CDN – Custom Builds, Compression, and Fast Performance

Tuesday, November 18th, 2008

We are pleased to announce that Ext has partnered with CacheFly, a global content network, to provide free CDN hosting for the Ext JS framework. Cachefly’s globally distributed network and aggressive caching accelerate the delivery of web content like JavaScript and CSS, making for an even faster Ext experience.

The Ext CDN also provides the ability to create your own custom builds using Ext’s Build It! tool, and host them on the CDN. The custom builder implements features to intelligently cache your component selections, adapter, and Ext version to create a unique custom build. These custom builds are cached across sessions and used by anyone who makes the same selections as you have – allowing for caching of custom builds across applications to fully realize the benefits of the CDN.

Creating a Custom Build

We’ve made the process of creating the custom build on the CDN as simple as a selecting the option.

Using the Custom build

To use your custom build on your own site, insert the output into the HEAD section of your site. If you needed to use a build with no grid or tree support you would just paste the following:

 <script type="text/javascript" src="http://extjs.cachefly.net/builds/ext-cdn-7.js"> </script>
 <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2/resources/css/ext-all.css" />

For those of you that need the complete library and use ext-all.js and ext-all.css we have those available as well.

 <script type="text/javascript" src="http://extjs.cachefly.net/ext-2.2/ext-all.js"> </script>
 <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2/resources/css/ext-all.css" />

Summary

There are many ways to judge an application’s performance, however none are as noticeable as the time it takes for an application to load. There are many techniques such as compression using gzip , minification using JSMin, and tools like YSlow to help developers make noticeable improvements. We hope the Ext CDN is another optimization our community will add to their toolbox.

Testing Ext JS & Ext GWT Applications With Selenium

Monday, November 3rd, 2008

Overview

As developers we can create great software. Unfortunately, we usually introduce a few bugs along the way. Using a testing tool can ensure we catch the bugs and resolve them quickly.

There are many different approaches to testing. Some advocate writing the tests within the application, others suggest a separate testing environment entirely. There are merits to both these approaches. For this blog post, I’ve focused our attention on Selenium as both Ext JS and Ext GWT and benifit from this “black box” testing methodology.

Selenium provides a powerful mechanism to test your Ext applications. Selenium works by executing tests against your running application within the browser of your choice. Selenium tests emulate the way a user would interact with your application by executing JavaScript to simulate user actions. Selenium tests run as a form of “integration” tests as they execute against your running application.

Both Ext JS and Ext GWT applications can benefit from Selenium tests. In fact, with few exceptions, the tests created for one product should be interchangeable as both products produce the same DOM structure.

With GXT applications, GWT provides built in JUnit support. This provides a great way to test your application. However, these tests run only in host mode. Being able to test your compiled application in multiple browsers is important as some issues only appear within your compiled application.

In general, you create Selenium tests and then execute them in a variety of ways. This tutorial will demonstrate creating tests with Selenium IDE, a Firefox plugin, and creating tests within Java. Tests will be loaded and executed within the Selenium IDE, and Java JUnit tests will be executed using Selenium Remote Control.

Selenium IDE

Selenium IDE is a Firefox extension that allows you to create, edit, and execute your tests. Tests can either be created manually, or by “recording” your actions. Recording can help you get a feel on how the Selenium commands are generated, but in most cases you will want to tweak the generated commands.

A Selenium test is a list of commands. Commands can be seen as actions, such as “click this element”, “type into this field”, and “assert an element exists”.

Install Selenium IDE

First, you will need to install the plugin which can be found here. Once installed you can choose Tools / Selenium IDE or View / Sidebar / Selenium IDE from the Firefox application menu.

Spend some time playing around with Selenium IDE. You can use the record button (red circle) to have the tool record your actions or you can enter commands manually. Most commands require a locator string. The locater string is repsonsible for identifying elements. There are various “types” of locator strings which are covered later.

Using Selenium IDE you can save and load tests. Also, notice the source tab. This tab allows you to view the test source. Selenium tests are saved as HTML files and can be exported to multiple server-side languages PHP, Ruby, Java, C#, Perl, and Python.

Creating a new Selenium Test

In this example, we will be testing both an Ext JS and Ext GWT form and it’s fields. First, let us take a look at the example code we will be testing. You can find the source code here:

Rather than creating a test from scratch, we will load an existing test file (listed above). Download this either the Ext JS or Ext GWT file to your file system. Then open the test in the Selenium IDE by selecting File / Open. Once loaded, Selenium IDE should look this this:

Notice the list of commands. Take a look at each command to get a feel of what the test is doing. If you opened Selenium IDE as seperate window, close it. Then open Selenium IDE using View / Sidebar / Selenium IDE. This will place selenium in the sidebar making it easier to run and monitor tests.

You can execute the test by clicking the first icon with the green arrow in the tool bar. Notice that you can control execution speed, use break points, execute individual commands, etc. After running your test, you screen should look like this:

Notice the form fields have been filled out. Examine the commands closely to see what actions were taken and what assertions where made. This test only touches the surface of things you can accomplish with commands. Note: When running the Ext JS test file, the radios and check boxes will not show the checked state, however, the true state will be correct, and the tests will run successfully.

Selenium Locators

Many Selenium commands require a locator to be specified. A locator is a way to identify an element in the page (the DOM to be specific). There are various types of locators including id, name, dom, xpath, link, and css. For this tutorial, XPath expressions where used. XPath expressions provide a powerful mechanism to identify elements. Example expressions look like this:

//input[@name='name']
//input[@name='name' and contains(@class, 'x-form-invalid')]
//input[@name='company']/following-sibling::img
//div[@id='Apple']
//div[@class='x-combo-list-item'][3]

Here are a few examples using CSS selectors:

css=html:root
css=div#structuralPseudo :nth-last-child(2)
css=a[class~="class2"]

Executing Tests with Selenium Remote Control (RC)

Selenium IDE provides a great way to create your tests and execute them in Firefox. Tests can only be run by manually opening Firefox and executing tests. What if you want to run you want to automate your tests and run them in other browsers? This is where Selenium Remote Control comes into play.

From the Selenium website: "Selenium Remote Control (RC) is a test tool that allows you to write automated web application UI tests in any programming language against any HTTP website using any mainstream JavaScript-enabled browser. Selenium RC comes in two parts. 1. A server which automatically launches and kills browsers, and acts as a HTTP proxy for web requests from them. 2. Client libraries for your favorite computer language."

Download Selenium RC

In addition to Selenium IDE, Selenium RC must be downloaded and can be found here. After downloading, unzip the file to your file system.

Starting the Server

The first step in using Selenium is to start the server. The server is responsible for executing your tests. The server will open browser instances, run tests, and close browsers. The server is written in Java and can be started by executing this command:

java -jar selenium-server.jar

The command should be executed from the folder where the selenium-server.jar is located.

To run your tests in Java, you must reference the Java client library. See selenium-java-client-driver.jar in the Selenium RC download.

Creating a test in Java

Selenium RC supports various languages, we will focus on Java. Rather than creating the Java test from scratch, Selenium IDE can be used to create the base Java code needed to create the test. From Selenium IDE, select the source tab, then choose Options / Format / Java. You will see the test is converted form HTML to Java. You can then copy and past the Java code to be used in your Java test. You can use the same steps to create code in other languages such as PHP and Ruby.

Here is the Java code that was created using the template code from Selenium IDE:

public class SeleniumTestCase extends SeleneseTestCase  {
 
  private Selenium selenium;
 
  public void setUp() {
    selenium = new DefaultSelenium("localhost", 4444, "*iexplore", "http://extjs.com");
    selenium.start();
  }
 
  public void testForm() {
    selenium.open("http://extjs.com/playpen/gxt/selenium/");
    pause(500);
    selenium.type("//input[@name='name']", "John");
    selenium.fireEvent("//input[@name='name']", "blur");
    assertTrue(selenium.isElementPresent("//input[@name='name' and contains(@class, 'x-form-invalid')]"));
    selenium.type("//input[@name='name']", "Darrell");
    selenium.fireEvent("//input[@name='name']", "blur");
    assertFalse(selenium.isElementPresent("//input[@name='name' and contains(@class, 'x-form-invalid')]"));
    selenium.type("//input[@name='email']", "darrell@foo.com");
    assertEquals("Darrell", selenium.getValue("//input[@name='name']"));
    assertEquals("darrell@foo.com", selenium.getValue("//input[@name='email']"));
    selenium.focus("//input[@name='company']");
    selenium.click("//input[@name='company']/following-sibling::img");
    assertEquals("43", selenium.getXpathCount("//div[@class='x-combo-list-item']"));
    selenium.click("//div[@id='Apple']");
    assertEquals("Apple Inc.", selenium.getValue("//input[@name='company']"));
    selenium.click("//input[@name='birthday']/following-sibling::img");
    selenium.click("//button[contains(text(), \"Today\")]");
    assertTrue(selenium.getValue("//input[@name='birthday']").matches("^[\\s\\S]*$"));
    selenium.check("//input[@value='Classical']");
    assertEquals("on", selenium.getValue("//input[@value='Classical']"));
    selenium.check("//input[@value='Blue']");
    assertEquals("on", selenium.getValue("//input[@value='Blue']"));
  }
 
  public void tearDown() {
    selenium.stop();
  }
 
}

Notice the use of "pause" after opening the application. This is needed to allow the GWT application to load. Other than that, the test code is a direct paste from the code generated in Selenium IDE. Notice that the test is using "*explorer" to execute the test. Firefox, Opera, Safari, Chrome are also supported. Keep in mind that the browser must be installed on the same maching where the Selnium server is running.

The new JUnit test can be executed just like any other JUnit test. The test can be executed with Eclipse.

Selenium Grid

Although it is not used in the tutorial, Selenium Grid is worth mentioning. From the website: "Based on the excellent Selenium web testing tool, Selenium Grid allows you to run multiple instances of Selenium Remote Control in parallel. Even better, it makes all these Selenium Remote Controls appear as a single one, so your tests do not have to worry about the actual infrastructure. Selenium Grid cuts down on the time required to run a Selenium test suite to a fraction of the time that a single instance of Selenium instance would take to run."

Summary

The tutorial demonstrated how load and run tests within Selenium IDE. The test code was ported to Java where it was run using Selenium Remote Control in a JUnit. This tutorial demonstrated the major moving parts involved in Selenium tests. It is recommended to take a look at the Selenium documentation and tutorials for more information.

Many thanks go out to the Selenium Team. There is a slight learning curve getting familiar with the product but I found it straightforward. The time was well spent as it opens up a new approach to testing web applications.

JackBe partners with Ext JS to offer Presto DE

Wednesday, October 29th, 2008

We are excited to be a part of JackBe’s announcement this week of both the Mashup Developer Community (MDC) and the free Presto Enterprise Mashup Developer Edition (DE). JackBe, the leading provider of enterprise mashup software, partnered with Ext JS to support the creation of sophisticated mashup widgets. By JackBe leveraging Ext JS, our community now has the added benefit of being “mashup-ready” developers.

Getting Started

Much of JackBe’s mashup UI technology is based on Ext JS. As an Ext JS developer, you’ll be right at home developing custom Mashlets using all your Ext JS knowledge. JackBe has some great examples of Ext-based mashlets, including a Competitive Intelligence mashlet, and their very popular ‘People Finder’ mashlet. My personal favorite was the sample mashlet application “Mashboard”.

Putting It Together

Presto includes a Mashlet Wizard that creates templated mashlets in a few clicks. But the real power is when you get your hands dirty and write plain-ole Ext JS code. The learning curve for an Ext JS developer is a few hours.

Every call to a mashlet will query the latest data from all of the data sources in the mashup, apply all of the sorts, merge, join/filter/extract operations you have specified, and render your prebuilt Ext interface with that new data. Many mashlets are also syndicatable and are accessed as simply as a script tag which can run in any browser. They can also be published as a JSR-168 portlet, or a widget in Netvibes or iGoogle, and even run on the iPhone. Mashlets are delivered from the Presto Mashup Server and hides all the security and governance required of most enterprise apps, particularly the ones that connect to many underlying data sources.

Interesting Stats

Gartner recently rated Enterprise Mashups as a Top IT Technology for the second straight year. In addition, Forrester estimates that mashups will be a $700 million market in 5 years. Therefore, Ext JS developers have the skills today to participate in a rapidly growing market.

Summary

We are thrilled JackBe selected Ext JS as their UI technology to, as they put it, ‘give Mashups a face’ . Deepak Alur, the VP of JackBe engineering said, “Partnering with Ext JS and using Ext JS javascript library as a foundation for our Mashlet technology was a no-brainer. It takes only a few minutes to see how powerful and enterprise-ready Ext JS is. It’s a perfect fit for us and our mashup developer community.”

I’ve been perusing JackBe’s Mashup Developer Community and also been getting personal demos from the JackBe. I am impressed with the speed and power you get from Presto Mashups and Mashlets. I recommend you join the MDC and download Presto DE. JackBe has started a Mash for Cash contest to find the best mashup developers. Our community has the leg up mashing with Presto and Ext JS. So, get to Mashing!

Extending Reader and Proxy – An Ext Flickr Image Gallery

Friday, October 17th, 2008

With the growing popularity of Web 2.0, more companies are providing public APIs for developers to create new and exciting compilations. These APIs can be used to grab small snippets of content which can be used in an application or site. Commonly referred to as a mashup, content from different sources are collected and presented to the user in a single interface. By aggregating the data in a single place, users remain on your site and need not be inconvenienced by multiple interfaces.

Flickr is a popular image sharing management tool. Amongst a number of services, it allows users to create online photo albums and the ability to categorize and tag photos. In this post we’ll look at retrieving data from Flickr by extending Ext’s native data package to display an image gallery. The image gallery widget will support the ability to search images based on how they are tagged inside Flickr.

Getting Started

To begin using the Flickr web services, we need to get an API key. The API key is sent with all requests and is used to identify the caller. Instructions on obtaining a key can be found here (there is no cost to get one). Once we have our key, we can look at the various methods the service exposes to us, which can be broadly classified as blog information, user information, contact information and photos. It is important to note that a lot of the methods in the Flickr API require authentication via a username and password. In our example, we will be using a public method only, which can be run without needing to be authenticated.

Cross domain restrictions and workarounds

Due to security restrictions in web browsers, Ajax requests can only be made to the domain of origin. Practically, this means we can’t make a call to another domain to request data. This poses a problem for building our image gallery. Enter Ext.data.ScriptTagProxy! ScriptTagProxy uses a technique called JSONP or JSON with padding that bypasses cross-domain restrictions by using script tags to transport data. It works by dynamically creating a new function in the window scope of the document. It then injects a script tag into the head of the document pointing at the remote resource with a url parameter of the name of the function it just created. The server-side must then use this url parameter to wrap around an object literal. Check out the Ext documentation for a Java Example.

Helper Classes

For the image gallery, I’ve written 2 small classes to assist with loading data from Flickr. The first is the FlickrProxy, which handles simple tasks like appending the api key and the method to each request. The second class is the FlickrPhotoReader, which defines the response format of photos. There are a number of different objects that get returned, so additional readers could be created for that purpose. The other class created is a FlickrWindow class. It is used to do basic paging operations and handle searching for images by tags.

Putting It All Together

Now we have all our components, we can use our new gallery. The code to create it is as follows:

var store = new Ext.data.Store({
    reader: new Flickr.FlickrPhotoReader(),
    proxy: new Flickr.FlickrProxy({
        apiKey: 'd4396e08e2a00f2c913d1fe5aa040c16',
        method: 'photos.search'
    })
});
 
var win = new Flickr.FlickrWindow({
    store: store,
    width: 400,
    height: 400
});
win.show();

The example code has been packaged as a zip and is available here.

After populating our data store, we can now navigate between the images. Some images are available in multiple sizes. You can use the image menu drop down to select what size you’d like displayed. If an image is not available in the size you have chosen you will receive a white image back with the text “This photo is currently unavailable.”

Next Steps

This example only uses a very small portion of the Flickr API. You can use the FlickrProxy as a base for accessing other methods offered in the Flickr API. The methods which require user authentication could prove to be particularly interesting for those of you who use Flickr on a regular basis. We’re always amazed at the wealth of talent and creativity in the Ext community and would love to see what you can create withthis small stepping stone into Flickr’s data services.

Ext Charting and Mapping with Google Visualizations

Monday, October 13th, 2008

Daily Forum Ratio GaugesCreating cross-browser consistent visualizations of data without Adobe’s Flash plugin has always been a difficult issue to address. Google introduced a Visualization API earlier this year which enables you to present tabular data in the form of charts, maps, and other graphical representations without the need for Flash. (Some visualizations actually do use flash, but most are implemented with SVG and/or VML.)

Working with different API’s can present hurdles as we attempt to massage the same data in two different data structures – one for a grid and another for a pie chart. To address this specific challenge, I developed a short user extension Ext.ux.GVisualizationPanel enabling users to integrate visualizations into Ext JS applications without concern for these issues. The GVisualizationPanel adapts any Ext data Store into the google’s format and enables you to embed any type of visualization into a panel.

Adapting google’s DataTable to an Ext Store

All Google Visualization’s are backed by a google.visualization.DataTable. According to Google, “A DataTable is a two dimensional table, with rows and columns and cells. Each column has a defined data type.” Google’s DataTable and Ext’s data Store are analogous and serve similar purposes. Their primary purpose is to enable developers to manipulate data in a single place and drive your GUI from that data. In order to continue changing our data in a single place and avoid having to manually synchronize 2 data structures, we will need to create an adapter to convert any Ext Store to a Google Table.

Differences between Ext.data.Store and google.visualization.DataTable

A key difference between Ext’s Store and Google’s DataTable is that the Ext data Store only handles data & schema information. On the contrary, Google’s DataTable requires presentation information like Label’s. The Ext.ux.GDataTableAdapter eliminates any inconsistencies between the 2 API’s allowing you to apply the same store to a visualization as you would apply to an Ext GridPanel. Ext.data.Store provides the developer with more formats by choosing a JsonReader, XmlReader or ArrayReader. Google’s DataTable is limited to programmatically adding data via methods or loading data from a Google Spreadsheet. This makes the Ext.data.Store a more favorable data structure in most cases. A final difference between Ext.data.Store and Google DataTable is that they each use different data types.

Using the Adapter

By eliminating the differences between Store and Table with the adapter, a single Ext.data.Store can be bound to Ext components and Google Visualizations at the same time. The Organizational Chart sample demonstrates how to synchronize a Google OrganizationChart and an Ext grid by binding them to the same Ext.data.Store. The sample also demonstrates how to consume the ’select’ event exposed by the GVisualizationPanel.

Google Orgchart Screenshot with Binding

Adapting your existing Ext Store’s can be done with Ext.ux.GDataTableAdapter’s static adapt method.

var dataTable = Ext.ux.GDataTableAdapter.adapt({
	store: myDs,
	columns: [{
		dataIndex: 'yr',
		label: 'Year'
	},{
		dataIndex: 'sales',
		label: 'Sales'
	},{
		dataIndex: 'expenses',
		label: 'Expenses'
	}]
});


Using the GVisualizationPanel

GVisualizationPanel works like any typical Ext.Panel and can partake in the container model and layout management. This means you can integrate it into your existing border layout or as a custom portlet. The class has been registered with the xtype of ‘gvisualization’. To use a visualization you will need to determine the visualizationPkg you’d like to use from Google. Then setup an appropriate store and pass in the visualizationPkg, store and columns configuration to create a new visualization.

For example, to create the Intensity Map used in the demo we can use the following code:

var countryStore = new Ext.data.SimpleStore({
    fields: [{
        name: 'Country',
        type: 'string'
    },{
 
        name: 'pop',
        type: 'int'
    },{
        name: 'area',
        type: 'int'
    }],
    data: [
        ['CN', 1324, 9640821],        
        ['IN', 1134, 3287263],
        ['US', 304, 9629091],
        ['ID', 232, 1904569],
        ['BR', 187, 8514877]        
    ]
});
var intensityMap = new Ext.ux.GVisualizationPanel({
    id: 'intensityMap',
    visualizationPkg: 'intensitymap',
    title: 'Intensity Map Sample',
    store: countryStore,
    columns: ['Country',{
        dataIndex: 'pop',
        label: 'Population (mil)'
    },{
        dataIndex: 'area',
        label: 'Area (km2)'
    }]
});



In Conclusion

Using the GDataTableAdapter to adapt or convert an Ext.data.Store to a google.visualization.DataTable is a good way to allow Ext Developers to use Google Visualizations without worrying about any underlying differences. Some of Google’s Visualizations have additional configuration options which can be used through a visualizationCfg configuration. GVisualizationPanel is a powerful implementation which supports any of the visualizations provided by Google in their gallery by simply setting a visualizationPkg config. Take a look at the many different types of visualizations available in Google’s Visualization Gallery.

Improving Application Usability with Ext JS Keyboard Handling

Tuesday, September 23rd, 2008

As Enterprise applications begin moving to the web instead of the desktop many developers may forget about providing key bindings for their applications. Most web applications use the keyboard only for text entry and do not associate particular key combinations with user actions. By providing this type of key handling, particularly for applications which require a lot of data entry, we can improve the end-user experience.

While at a recent client engagement, we wanted to provide a smooth transition for users skilled at working with a legacy ‘green screen’ application to using a new Ext JS version. Part of this transition involved enabling power users to continue navigate the application with the same key handling. By the end-users not having to remove their hands from the keyboard, their efficiency increases substantially. Here’s some insight into how we accomplished the task at hand.

Ext.KeyMap

Ext provides several components that support keyboard navigation out of the box such as GridPanel, ComboBox, and TreePanel. To implement custom keyboard handling, developers can use the Ext.KeyMap and Ext.KeyNav classes to attach keyboard bindings to any component or element they wish.

The first set of keys we wanted to handle was all of the Function keys (F1-12). While most browsers reserve some/all of these keys, with some ext-pertise, we are able to override the default function if need be for our application. The application we were working with was completely dynamic and server driven, so we really couldn’t define all of the handlers ahead of time. This led to us dynamically building an array of key handler configuration objects and passing them all through to our new Ext.KeyMap object.

var keys = [];
for(var i = 0;i < buttons.length;i++) {
    var btn = buttons[i];
    // fkey property contains a string referencing the Ext constants (ie: Ext.EventObject.F1)
    var fk = eval(button.fkey);
    btn.handler = this.handleKey.createDelegate(this, [fk]);
 
    keys.push({
        key: fk,
        handler: this.handleKey.createDelegate(this, [fk]),
        stopEvent: true,
        scope: this
    });
}

This implementation demonstrates creating a toolbar and assigning function keys that would perform the same action when the button was clicked or when using the mouse. buttons is an array of button configurations that we pulled from the Json response from the server. The server is sending over a string which references some constants built into the Ext.EventObject for function keys. Since the data comes over as a string we can simply eval it and get the resulting number.

We use a generic handler for all of the click and keypress functions since it is all controlled from the server. createDelegate lets us pass along the key that was pressed so that we can share the same handler function regardless of if the event was a click or a keypress. With the keys array built and our handlers added into the buttons array, we are able to toss these into our Panel configuration and it will do the heavy lifting for us.

tabPanel.add({
    title: 'Test Panel',
    tbar: buttons,
    keys: keys
    ...
});

With our layout in place, adding a tab with our key handlers is pretty simple. The tbar parameter takes our array of Ext.Button configs that were server generated (aside from the hand