Anyone new to using the Ext library or trying to learn more about it
has come to the right place. This tutorial will walk through Ext basic
concepts and how to get a dynamic page up and running quickly. It is
assumed that the reader has some Javascript experience and a basic
understanding of the HTML document object model (DOM).
Introduction to Ext 2.0
Anyone new to using the Ext library or trying to learn more about it
has come to the right place. This tutorial will walk through Ext basic
concepts and how to get a dynamic page up and running quickly. It is
assumed that the reader has some Javascript experience and a basic
understanding of the HTML document object model (DOM).
Set Up Ext
If you haven't done so already, you'll first want to get Ext set up.
Let's Get Started
We're going to walk through some of the most common tasks that
people have to accomplish in Javascript and how to perform them using
Ext.
This zip contains four files: ExtStart.html, ExtStart.js, ExtStart.css and ajax-example.php.
- Unzip all 4 files into a folder directly under the folder where Ext is installed (for example, if Ext is at "C:\code\ext-2.0\," create a new folder under "ext-2.0\" called "tutorial" ie. "C:\code\ext-2.0\tutorial\").
- Double-click ExtStart.html so that it launches in
your default web browser, and you should get a message telling you that
everything is configured correctly. If you get a JavaScript error,
please follow the instructions on that page to get it working.
Now you're ready to open ExtStart.js in your favorite IDE or text editor and take a look at it:
Ext.onReady(function() {
alert("Congratulations! You have Ext configured correctly!");
});
Ext.onReady is probably the first method that you'll use on every
page. This method is automatically called once the DOM is fully loaded,
guaranteeing that any page elements that you may want to reference will
be available when the script runs. You can go ahead and delete the
alert() line now so that we can start adding some real code that
actually does something useful!
Element: The Heart of Ext
Almost everything you do in Javascript will at some point involve
referencing specific elements on your page so that you can do
interesting things with them. Using traditional Javascript, selecting a
DOM node by ID is done like this:
var myDiv = document.getElementById('myDiv');
This works just fine, but the object that is returned (a DOM node)
doesn't offer much in the way of power or convenience. In order to do
anything useful with that node, you are left still writing a lot of
custom plumbing code yourself. Plus it is your responsibility to handle
all of the differences in how the node can be used from browser to
browser, which can be daunting.
Enter the Ext.Element object. The Element really is the heart
of Ext as most of what you'll do involves getting access to Elements
and performing actions on them. The Element API is fundamental to the
entire Ext library, and if you spend the time to really learn only one
class in Ext well, Element should be it!
The corresponding code to get an Ext Element by ID looks like
this (the starter page ExtStart.html contains a div with the id
"myDiv," so go ahead and add this code to ExtStart.js):
Ext.onReady(function() {
var myDiv = Ext.get('myDiv');
});
So we are getting back an Element object now—what's so interesting about that?
- Element wraps most of the DOM methods and properties that
you'll need, providing a convenient, unified, cross-browser DOM
interface (and you can still get direct access to the underlying DOM
node when you need it via Element.dom)
- The Element.get() method provides internal caching, so multiple calls to retrieve the same object are incredibly fast
- The
most common actions performed on DOM nodes are built into direct,
cross-browser Element methods (add/remove CSS classes, add/remove event
handlers, positioning, sizing, animation, drag/drop, etc.)
This means that you can do all kinds of useful stuff with very minimal code. Here are just a few simple examples. Go ahead and try
adding some of these to ExtStart.js after the previous line where we
got the 'myDiv' Element:
myDiv.highlight(); // The element's background will highlight to yellow then fade back
myDiv.addClass('red'); // Add a custom CSS class (defined in ExtStart.css)
myDiv.center(); // Center the element in the viewport
myDiv.setOpacity(.25); // Make the element partially-transparent
Selecting DOM Nodes
Often it is either impractical or impossible to select DOM nodes by
ID. Maybe the ID is not set, or you don't know it, or there are too
many elements to practically reference directly by ID. Sometimes you
may want to select nodes based on something other than ID, like an
attribute or a CSS classname. For these reasons, Ext ships with an
extremely powerful DOM selector library called DomQuery.
DomQuery can be used as a standalone library, but more often when using Ext,
you'll use it in the context of selecting Elements so that you can then
act on them via the Element interface. Luckily, the Element object
itself supports querying via the Element.select method, which
internally uses DomQuery to select elements. As a simple example of how
you might use this, the ExtStart.html file contains several paragraph
(
) tags, none of which have ids. If you wanted to easily
select every paragraph and perform an action on all of them at once,
you could do something like this:
// Highlights every paragraph
Ext.select('p').highlight();
DomQuery supports a wide array of selection options, including
most of the W3C CSS3 DOM selectors, basic XPath, HTML attributes and a
lot more.
Responding to Events
So far in our examples, all of the code we've written has been
directly inside the onReady function, which means that it always
executes immediately after the page loads. This doesn't give us much
control—you will most commonly want your code to execute in response to
specific actions or events that you choose to handle. To do this, you
define event handlers that can respond to events using functions that
you assign.
Let's start off with a simple example. Open up ExtStart.js and edit it so that your code looks like this:
Ext.onReady(function() {
Ext.get('myButton').on('click', function(){
alert("You clicked the button");
});
});
The code still executes when the page loads, but there's an
important difference. The function containing the alert() is defined,
but is not actually executed immediately—it is assigned as the
"handler" of the button click event. Spelled out in plain English, this
code might read: "Get a reference to the Element with id 'myButton' and assign a function to be called anytime someone clicks on the Element."
Not surprisingly, Element.select allows you to do the same
thing, but with an entire group of Elements at once. For example, to
show our message when any paragraph in our test page is clicked, we could do this:
Ext.onReady(function() {
Ext.select('p').on('click', function() {
alert("You clicked a paragraph");
});
});
In these two examples, the event handling function is simply
declared inline, without giving it a function name. The term for this
type of function is an "anonymous function" since it is declared
without ever being named. You can also assign an event to be handled by
a named function, which is especially useful if you want to reuse the
function and have it handle multiple events. For example, this code is
functionally equivalent to the previous example:
Ext.onReady(function() {
var paragraphClicked = function() {
alert("You clicked a paragraph");
}
Ext.select('p').on('click', paragraphClicked);
});
So far we have looked at performing a generic action when our event
is raised, but how do we actually know which specific Element raised
the event so that we can perform some action on it? It turns out to be
pretty easy—the
Element.on method passes three extremely useful parameters to the event handling
function (we're only going to look at the first one here, but you
should explore the API documentation to learn more about event handling
details). In our previous examples our handling function was ignoring
these parameters, but with one simple change, we can provide an
additional level of functionality. The first, and most important,
parameter is the event that occurred. This is actually an
Ext,EvenObject, which is both normalized across browsers and provides more information
than the standard browser event. For example, the event's target DOM
node can be retrieved with this simple addition:
Ext.onReady(function() {
var paragraphClicked = function(e) {
Ext.get(e.target).highlight();
}
Ext.select('p').on('click', paragraphClicked);
});
Note that target is a DOM node, so we first retrieve the
corresponding Element, then perform whatever action we want on it. In
this case, we are visually highlighting the paragraph.
Passing Arguments to Event Handlers
var someHandler = function(evt,t,o,myArg1,myArg2,myArg3) {
//do stuff
}
Ext.select('.notice-type1').addListener('click', someHandler.createDelegate(this, [4,'pizza',11], true));
Ext.select('.notice-type2').addListener('click', someHandler.createDelegate(this, [7,'stuff',12], true));
The function someHandler will get called with the following arguments:
- evt, t, o
- These are the arguments that would get
passed to your event handler normally. They are: the EventObject
describing the event, the Element which was the target of the event,
and the options object from the addListener call. You can also set a breakpoint in
Firebug inside your event handler to check these objects out and see
what kind of useful things they contain.
- myArg1, myArg2, myArg3
- your own custom arguments
specified as an array which is passed as the second argument to
createDelegate. In this case we're passing 4,'pizza',11 for elements of
class "notice-type1" and 7,'stuff',12 for elements of class
"notice-type2".
If all you want to do is pass custom arguments to the even handler
and you don't need to have access to the element that fired the event
you're probably better off just using createCallback
Using Widgets
In addition to the core javascript library that we've been
discussing, Ext also includes one of the richest sets of Javascript UI
widgets available today. There are far too many to cover in this
introduction, but let's take a look at a couple of the widgets that
people use most commonly and how easy they are to work with.
MessageBox
Rather than a boring "Hello World" message box, let's add a little
twist. We already have code that we wrote in the previous section that
highlights each paragraph when you click on it. Let's modify that code
to also show the text of the paragraph that was clicked in a message
box. In the paragraphClicked function above, replace the line:
Ext.get(e.target).highlight();
...with this code:
var paragraph = Ext.get(e.target);
paragraph.highlight();
Ext.MessageBox.show({
title: 'Paragraph Clicked',
msg: paragraph.dom.innerHTML,
width:400,
buttons: Ext.MessageBox.OK,
animEl: paragraph
});
There are a couple of new concepts being shown here that are worth
discussing. In the first line, we are now creating a local variable
named paragraph that will hold a reference to the
Element representing the DOM node that was clicked (in this case we
know it will always be a paragraph since our click event is only
associated with
tags). Why are we doing this? Well, looking
ahead for a moment, we will need a reference to the Element to
highlight it, and we'll also use the same Element for some of the
MessageBox parameters. In general, it is bad practice to make the same
function call multiple times to retrieve the same value or object
reference, so by assigning it to a local variable and reusing the
variable, we are being good object-oriented developers!
Now, on to the MessageBox call, which demonstrates the other
new concept for us to discuss. At first glance, this may look simply
like a list of parameters being passed to a method, but if you look
closely, there is a very specific syntax. What is actually being passed
to MessageBox.show() in this case is only one parameter: an object literal
that contains a set of properties and values. In JavaScript, an object
literal is a dynamic, generic object that is created anytime you use
the { and } characters surrounding a list of name/value properties, and
the format for those properties is [property name] : [property value].
You'll see this pattern used extensively throughout Ext, so you should
learn it well!
Why use an object literal? The main reason is flexibility. New
properties can be added or removed from the object literal at any time,
or defined in any order, while the method signature (the number and
types of parameters expected by a method) never has to change. It also
makes it far more convenient from the end developer's perspective when
using methods with many optional parameters (as in the case of
MessageBox.show). For example, let's say that a fictional method
foo.action has four optional parameters, but you only need to pass one
of them. In this case, your code might look like this: foo.action(null,
null, null, 'hello'). However, if that method instead took an object
literal, the code would look like this: foo.action({ param4: 'hello'
}). Much easier to use, much more readable.
Grid
The grid is one of the most popular widgets in Ext, and usually the
first one that people want to see, so let's take a look at how easy it
is to get a basic grid up and running. Replace any existing code you
have in ExtStart.js so that it looks like this:
Ext.onReady(function() {
var myData = [
['Apple',29.89,0.24,0.81,'9/1 12:00am'],
['Ext',83.81,0.28,0.34,'9/12 12:00am'],
['Google',71.72,0.02,0.03,'10/1 12:00am'],
['Microsoft',52.55,0.01,0.02,'7/4 12:00am'],
['Yahoo!',29.01,0.42,1.47,'5/22 12:00am']
];
var myReader = new Ext.data.ArrayReader({}, [
{name: 'company'},
{name: 'price', type: 'float'},
{name: 'change', type: 'float'},
{name: 'pctChange', type: 'float'},
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
]);
var grid = new Ext.grid.GridPanel({
store: new Ext.data.Store({
data: myData,
reader: myReader
}),
columns: [
{header: 'Company', width: 120, sortable: true, dataIndex: 'company'},
{header: 'Price', width: 90, sortable: true, dataIndex: 'price'},
{header: 'Change', width: 90, sortable: true, dataIndex: 'change'},
{header: '% Change', width: 90, sortable: true, dataIndex: 'pctChange'},
{header: 'Last Updated', width: 120, sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'}
],
viewConfig: {
forceFit: true
},
renderTo: 'content',
title: 'My First Grid',
width: 500,
autoHeight: true,
frame: true
});
grid.getSelectionModel().selectFirstRow();
});
While this looks like a lot, it is really only 4 lines of code in total (3 if you don't count the test data)!
- The first line of code creates an array of test data to be
displayed in the grid. In real projects, you would likely load this
data from some dynamic source like a database or web service.
- Next, we create a data reader that knows how to interpret
the data and convert it into usable records for the grid's data store.
There are several different types of Reader classes for different types
of data.
- Next, we create the grid widget, passing in all sorts of config values, including:
- A new data store, configured on the fly with our test data and reader
- The column model definition using the columns config
- Additional options to set up specific grid features
- Finally, we tell the grid to highlight its first row via the SelectionModel.
How easy was that? If all went well, you should end up with something that looks close to this:
Of course, there will probably be some details about this code that
you may not fully understand at this point. The intent of this example
is to show how it's possible to create an extremely rich,
visually-complex user interface component with very few lines of
code—learning the details will be left as an exercise for the reader.
There are many resources to help you with learning the grid, including
the interactive grid demos and the .Grid anel API documentation.
Using Ajax
Once you have your page created and you know how to interact with it
through Javascript, you'll probably want to know how to get data to and
from a remote server, most commonly to load and save data from a
database on the server. Doing this asynchronously via Javascript
without reloading the page is known commonly as Ajax, and Ext
has excellent Ajax support built right in. For example, a common goal
is to handle a user interaction, post something to the server
asynchronously, then update an element of the UI in response to the
action. Here's an example of a very simple HTML form containing a text
input field, a button, and a div used to display a message (Note:
you can add this code to ExtStart.html if you'd like to follow along,
but you'll have to have access to a web server in order to run the
server code below):
Name: type="text" id="name" />
type="button" id="okButton" value="OK" />
id="msg">
Next, we'll add the Javascript required to get our data and post it
to a server-based process (replace any existing code in ExtStart.js
with this):
Ext.onReady(function(){
Ext.get('okButton').on('click', function(){
var msg = Ext.get('msg');
msg.load({
url: 'ajax-example.php', // <-- change if necessary
params: 'name=' + Ext.get('name').dom.value,
text: 'Updating...'
});
msg.show();
});
});
Note: This example will only run from a web server. The URL in your browser should start with http:// and not file:// or the Ajax transaction will not work! Localhost will work fine, but it must be via http.
Hopefully the general pattern is starting to look familiar by now! The code is wrapping the okButton input with an Element object and attaching an anonymous function that will handle the event
if anyone clicks on the button. Inside the click handler, we're using a
special class built into Ext called the Update this
class makes sending an Ajax request, receiving a response and updating
another Element extremely trivial. The Updater can be used directly, or
as we're doing here, it can be accessed via the Element that we want to
update (in this case the 'msg' div) using the Element.load method. When Element.load is used, the server's response automatically
replaces the innerHTML of the Element. Simply pass it the URL to the
server-based process that will handle the request, the querystring
parameters to process (in this case passing in the value of the 'name'
field) and the text to display in the Element's innerHTML while the
request is being processed. Show the msg div (since it starts hidden by
default) and that's it! Of course, as with most things in Ext, there
are many more Updater options supported, as well as different ways to
process Ajax requests in different situations but this shows how easy it is to get a basic example up and running.
The last piece of the Ajax puzzle is the process on the web
server that actually handles the request and returns a response to the
page. This process could be a server page, a servlet, an HTTP handler,
a web service, even a Perl or CGI script—just about anything that can
reside on a web server and process HTTP requests. Unfortunately,
because of this variety there is no way to give a standard example that
would cover all possibilities. Here are some examples in a few common
languages to hopefully get you started (this code simply echoes
whatever was passed from the 'name' field back to the client with 'From
Server: ' added at the beginning, and that gets written to the 'msg'
div). The PHP example has been included in the download as
'ajax-example.php' but feel free to replace that with your server code
of choice:
Plain PHP
if(isset($_POST['name'])) {
echo 'From Server: '.$_POST['name'];
}
?>
CakePHP
if(isset($this->data['name'])) {
$this->flash('From Server: '.$this->data['name']);
}
?>
Django
from django.http import HttpResponse
def ajax_request(request):
return HttpResponse('From Server: %s' % request.POST.get('name', 'nada'))
Perl
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
my $Query = new CGI;
print $Query->header();
print "Hello from : ".$Query->param('name');
exit;
ASP.Net
protected void Page_Load(object sender, EventArgs e)
{
if (Request["name"] != null)
{
Response.Write("From Server: " + Request["name"]);
Response.End();
//Note: the call to Response.End() will throw an
//exception (that's what it's designed to do). It's
//OK to simply catch it and ignore it; depending on
//your use case, you may find that necessary.
}
}
ColdFusion
StructKeyExists(form, "name")>
From Server: #form.name#
or using ColdFusion Scripting (cfscript)
if (StructKeyExists(form, "name")) {
writeoutput("From Server : " & form.name);
}
JSTL (JSP)
From Server: ${param.name}
Ruby on Rails
render :text => "From Server: #{params[name]}"
The real challenges when dealing with Ajax processing involve
all of the plumbing code required to properly process and format real
structured data on the server. There are several formats to choose from
that people use commonly (most often either JSON or XML). There are
also many language-specific libraries available to deal with Ajax
processing that can work well with Ext, as Ext is language-neutral with
regard to the server. As long as the result is sent to the page in the
proper data format, Ext does not care what happens on the server!
|