Article
Learn Adobe AIR, Part II: Build a Customer Management App
In our previous tutorial, we created a personal notes storage database using HTML, CSS, and JavaScript. In this tutorial, we’re going to explore the UI features of the AIR platform by building a simple Customer Relationship Management (CRM) application. We’ll use those same technologies, as well as a local SQLite database.
Just as we did with Part 1, there’ll be another quiz to test you on what you’ve learned. Again, the first 100 people to undergo the quiz will receive, free-of-charge, the Adobe AIR For JavaScript Developers pocket guide in the post. Remember, the book can be downloaded free as a PDF for a short time too, so get cracking!
Building User Interfaces in AIR
Our standard web technologies—HTML, CSS, JavaScript—provide a solid foundation to build rich interfaces within the browser. However, sometimes we need to extend beyond the browser; this way we can take advantage of the standard interactions users are accustomed to with their desktop applications. This is especially important for any kind of data entry and reporting—and what better way to explore this than with a business data entry application.
In this article, we’ll explore the user interface possibilities that AIR provides. We’ll implement drag-and-drop functionality, work with native windows, communicate between windows, construct menus, and more. Once we’re finished, we’ll have built a simple yet powerful application for managing a customer records database.
Assumed Knowledge
Throughout this tutorial, I’ll assume you’ve read through the first article in this series, Learn Adobe AIR, Part 1: Build a Note Storage App, or at least are comfortable with creating simple applications in Adobe AIR. Once again, you’ll need the AIR SDK, Aptana, jQuery, the AIR menu builder, and the AIR Introspector. If you haven’t set these up, do so now. I covered how to do this in my first article, in the section: A Powerful Tool for Building AIR Apps.
Setting up the Example Project
To play along at home, download the skeleton AIR project for this tutorial and import it into Aptana.
If you’d rather create your project from scratch, make sure that you import jQuery by checking the Import Javascript Library option in the New Project wizard. Once the project has been created, you can safely remove the following files:
- AIRLocalizer.js
- AIRSourceViewer.js
- jquery_sample (note there are two files with this name)
- CRMTest.html (this filename may differ, depending on what you called your project)
- LocalFile.txt
- sample.css
We’re using jQuery 1.2.3, but any newer 1.2.x release should work fine. Add the contents of the zip file to the project directory, refresh the folder (right click the project name in the Project view and select Refresh), and you’re good to go.
Running on AIR
Now that you’ve got the example project setup, let’s get started. As I mentioned, we’re going to build a simple CRM application. Here’s how the app looks in Windows Vista:
![]()
Here’s the plan of attack:
- construct our menus
- establish a database connection to an SQLite database
- read our initial customer records
- create a form to view each record
- create a form to create new records
Along the way, we’ll learn how to manually construct menu trees, read directly from the file system, use prepared statements on SQL databases, and even implement native drag-and-drop functionality (which will work on any AIR-supported platform).
Creating Menus
In our previous AIR article, we used the menu builder framework to automatically create menus from an XML document. However, there are some limitations to this approach. This time around, we‘ll make use of the native menu API to construct our menus.
The main factor to consider when building menus at this level is the location of the menu that you’ll create. Options include the window menu, which is available at the top of the window in a Windows application, and the application menu, which sits at the top of the screen in most OS X applications. We can also use the same native menu system to create context menus, menus for dock icons (OS X), and menus for system tray icons (Windows). Luckily, regardless of the type of menu we want to build on, the syntax is the same, thanks to JavaScript’s object oriented nature. In Aptana, open the menus.js file and add the following code:
$(document).ready(function(){
if (air.NativeWindow.supportsMenu) {
nativeWindow.menu = new air.NativeMenu();
targetMenu = nativeWindow.menu;
}
if (air.NativeApplication.supportsMenu) {
targetMenu = air.NativeApplication.nativeApplication.menu;
}
});
The code above creates an object named targetMenu. This refers either to a menu on the nativeWindow (which, in a Win32 environment, is the current window), or the global application window (OS X). An AIR menu is a collection of objects for all menu items, governed by a main air.NativeMenu class. We can check the supportsMenu property of an AIR window or application object to determine what is supported by the current platform. Note that on OS X, the application menu already exists, regardless of whether a window exists or not. The Adobe AIR manual has further information on AIR menus.
Also remember that we’re using jQuery, which executes the following line of code whenever our page’s DOM has completed loading:
$(document).ready()
We’ve already created this function; let’s add to it now. Modify your menus.js file so that it looks as follows (the new lines are shown in bold below):
$(document).ready(function(){
if (air.NativeWindow.supportsMenu) {
nativeWindow.menu = new air.NativeMenu();
targetMenu = nativeWindow.menu;
}
if (air.NativeApplication.supportsMenu) {
targetMenu = air.NativeApplication.nativeApplication.menu;
}
var fileMenu;
fileMenu = targetMenu.addItem(new air.NativeMenuItem("File"));
fileMenu.submenu = new air.NativeMenu();
newCustomer = fileMenu.submenu.addItem(new air.NativeMenuItem("Create Record"));
newCustomer.addEventListener(air.Event.SELECT, Menu_NewCustomer);
newCustomer = fileMenu.submenu.addItem(new air.NativeMenuItem("Exit"));
newCustomer.addEventListener(air.Event.SELECT, Menu_Exit);
});
These few lines of code create a simple file menu, which contains two menu items: Create Record and Exit. We create new objects for each item manually, although this could be automated to suit the application.
There are a number of advantages to this approach, including the flexibility to add custom keyboard shortcuts, and perform custom actions when a menu item is selected. More importantly, however, we now have a basic infrastructure in place for our menu, to which we can easily add, remove, or modify items on the fly.
The last line specifies the callback function that will be executed when the Exit menu item is selected. (Note there’s also a DISPLAY event that is fired when the user opens the menu but hasn’t selected it yet). Those callback functions don’t exist yet, so let’s go ahead and write them now:
function Menu_NewCustomer() {
document.location = "new_customer.html";
}
function Menu_Exit() {
var event = new air.Event(air.Event.EXITING, false, true);
air.NativeApplication.nativeApplication.dispatchEvent(event);
if (!event.isDefaultPrevented()) {
air.NativeApplication.nativeApplication.exit();
}
}
The Menu_Exit method simply checks whether another part of the application (or the AIR runtime itself) is attempting to delay termination of the current application. In this way, it’s similar to the exit function in the previous article I wrote about Adobe AIR. It’s possible to override any delay that’s detected by this method, but it’s not recommended; that’s because it’s best practice to respect any code that is executing alongside yours in such a high-level framework.
Connecting to the SQL Database
In the skeleton project archive, I’ve included a sample SQLite database, named crm.sqlite. This database consists of just one table, which contains the following sample data (to view this database, I recommend SQLite Manager, a Firefox extension):
| ID | Name | Phone | Notes |
|---|---|---|---|
| 1 | John Citizen | (123) 456-7890 | ==Orders==\n\nCustomer ordered … |
| 2 | Jane Doe | (123) 456-7899 | Signed customer on 1/1. |
The example application that we’ll build in this tutorial will provide the user with an interface for accessing this information. We’ll build functionality for viewing individual records, importing data into the Notes column, and more. While the completed application will be quite simple, it would certainly be feasible to use it as a base for building a fully-fledged CRM application.
As we saw in the first tutorial, connecting from a single window to a local database is relatively easy. In this application, however, we’re using multiple windows, and each will operate independently. This means we’ll need to share this database connection among the various scripts throughout our application.
With this in mind, let’s establish a standard database connection—add the following code to your database.js file:
function SetupDB() {
var db = new air.SQLConnection();
var dbFile = air.File.applicationStorageDirectory.resolvePath("crm.sqlite");
//if (!dbFile.exists) {
var dbTemplate = air.File.applicationDirectory.resolvePath("crm.sqlite");
dbTemplate.copyTo(dbFile, true);
//}
try
{
db.open(dbFile, air.SQLMode.UPDATE);
}
catch (error)
{
air.trace("DB error:", error.message);
air.trace("Details:", error.details);
}
return db;
}
The code above defines a simple function, SetupDB(), which creates a connection to the local SQL database. As you’ll recall, the application directory is read-only (on a Windows machine this is the Program Files folder). You should therefore copy the skeleton database to the application storage directory (on Windows this is the AppData folder).
During the development phase, we’ll be making potentially destructive changes to our database, so it’s convenient to be able to recreate it each time we relaunch our application. When we deploy our app to production, we can simply uncomment the dbFile.exists condition, to prevent our database from being replaced.
Akash Mehta is a web developer and freelance writer specializing in web application development. Check out his other work at