Try out the next Form Builder

The purpose of this (small) update was to improve the form outline displayed on the left (the treeview). I rewrote the entire drag&drop functionality and added the possibility to expand/collapse form sections.

Give it a try and share your comments here. Thank you!

At the time I started working on the drag&drop feature I couldn’t find any good javascript library capable of sorting nested lists, so I ended-up writing my own. It’s now available as open-source (but not yet bug free).

The lastest version of the script.aculo.us library offers this functionality as well, so if you’re already using prototype, this is a good alternative.

Credits: some icons used in the form builder are courtesy of famfamfam.

March Madness

February is over and so far so good, the Form Assembly is on track. Over the last month a number of new features have been released: charts, an improved form builder, video tutorials and a spam/junk filter on the form processing service.

March is going to be hectic. First I’ll be presenting the Form Assembly at the Under The Radar Conference, in Mountain View, CA. Thanks a lot to Debbie Landa for the invitation! I’m looking forward meeting the other presenters and some of the best known people in the industry.

(On a side note, March 2nd will officialy put an end to 3 straight years without wearing a tie…)

Then I’m flying off to SXSW. If you happen to be there too and see a tall guy with an incomprehensible french accent, stop and say bonjour!

And before the month’s end, I’ll be off to some snowy slopes near Montreal. In between I hope to release wForms 2.0, the long planned update to the open-source javascript library for web forms.

Technorati Tags: , , ,

Treating the HTTP Status code right

If you’ve been following the whole Ajax thing for a while, you’ve certainly seen this few lines of code hundreds of times. You know, the ones that check the readyState and status properties of a xmlHttpRequest.

    if (req.readyState == 4) {
        if (req.status == 200) {
            // ...processing...
        } else {
            alert("There was a problem retrieving the XML data:n");
        }
    }

The status is the HTTP code returned by the web server (here 200, meaning that everything went fine). Normally, the status code is part of the private conversation between the browser and the web server. You don’t have to worry about it, unless of course you encounter the dreaded 404 Page Not Found.

Things change when you start developing Ajax-style with XmlHttpRequest. You are basically by-passing the browser native handling of HTTP and left dealing with the status code on your own. And, that’s a great opportunity. Why ? Because you can
use it to relay information about the execution of your server-side scripts.

The HTTP protocol defines many status codes. For a web developer some of the interesting ones are:

  • 200 Ok
  • 201 created
  • 400 bad request
  • 403 forbidden
  • 500 internal server error

Most server-side scripting languages will let you modify the http response header and set just about any status code. For instance in PHP, if you were processing a request that looked suspicious (a tempered query string?) you could do:

< ?php
header('HTTP/1.0 400 Bad Request');
die('sorry, the execution failed for some reason..');
?>

If you are processing a login/registration form, you could use header('HTTP/1.0 200 Ok') for a successful login, header('HTTP/1.0 201 Created') if a registration was ok and an account was created, and maybe header('HTTP/1.0 403 Forbidden') for a wrong password or if the username was already taken.

Back to the javascript code, here’s now a smarter response handler for the authentication example.

switch (req.status) {
    case 200:
            // login ok, moving on...
            break;
    case 201:
            // was a registration.. will show a welcome message.
           showWelcomeMessage(req.reponseText);
           break;
    case 403:
           // authentication problem. The error message is in responseText
          showErrorMessage(req.responseText);
          break;
    default:
         // unknown error
         alert("There was a problem..");
    }

Remark: Safari will return a ‘undefined’ status code if the response content is empty, so make sure to have at least a space in the response (using echo "" in PHP for instance).

That’s it! Happy coding in 2006.

The Freja Framework

Background

Since I started working on the Form Builder, almost a year ago, I’ve adopted a radically different approach to web development. At that time, I had firmly embraced the concept of separating structure from presentation using valid XHTML and CSS. The next logical step was for me to catch up with the experienced web developers who preached the separation of data, business logic and presentation. The Model-View-Controller pattern was (and still is) big, but I was more impressed by the REST philosophy and the Service-Oriented Architecture style.

I gave a shot at building an application - the form builder - that would follow these principles. Coded in Javascript, it runs on the client and deals with web services using asynchronous HTTP GETs and POSTs. I found that writing REST services was dead simple. It’s like writing a regular server-side script but it doesn’t need to spit out a HTML page. You just output some HTTP headers and either plain text or a XML string.

Now, since the web services didn’t provide HTML, the client - the web browser - had to create it on its own. XSLT was the perfect candidate. It was a web standard and it was supported by 90% of the browser market (IE5+, Netscape 7+ / Mozilla).

By the time I had the Form Builder working, the Ajax term was coined and I was able to put a name on what I was doing.

Fast forward 10 months: more browsers now support XSLT and XMLHTTPRequest (Opera 9, and Safari - somewhat - ). The Form Builder is now at version 2.0 and so is the Time-Tracker - a small app that started as a proof-of-concept.

But it’s time to introduce Freja.

Framework

Freja is the fruit of this learning process. It is a lightweight and simple Javascript framework that facilitates the development of single-screen, zero-latency web applications.

It isn’t yet-another-Ajax-framework. It does things that no other Ajax tool does and it doesn’t do what most others do. It’s a companion for libraries like prototype and maybe for more complex frameworks like Dojo.

Freja stands for ‘Framework for REstful Javascript Applications’. At its core it’s a XML / XSLT engine, but it’s all abstracted into the Model-View-Controller pattern.

  • A model is a XML document obtained from a URL (could be a web service or just a flat file).
  • A view is a XSL template obtained also from a URL.
  • The controller is the javascript code. Freja provides a set of convenient (did I say elegant?) functions for retrieving, rendering, updating and saving the models.

Learn More

Visit Freja’s home site for Documentation, Tutorials and download.

Technorati Tags: , , , ,

Type-Ahead Script Updated

One of the feature I decided early on to include in the Time-Tracker application was a type-ahead tool, akin to the famous Google Suggest. You can take a look at the old demo do get an idea of what I’m talking about.

After just a few weeks into the development of the Time-Tracker, the Type-Ahead script was ripe for refactoring. I needed a way to handle private suggestion lists as well as shared lists. I also wanted to use the same Type-Ahead object for several input fields.

The new version now runs smoothly in the Time-Tracker. Here’s how to use it in your own projects (licensed under the CC-GNU LGPL).

Source Files

Writing the server-side script in a different language is quite trivial. You’re welcome to share your version in the comment form below (or send me a mail).

Usage Notes

Add the following javascript code to your page to add the Type-Ahead behavior to any text field.

/* Wait for the page to finish loading */
XBrowserAddHandler(window,'load', function() {

/* Instanciate the TypeAhead object */
var typeAhead = new TypeAhead('listName', 'groupName');

/* Assign the TypeAhead Object to an input field */
typeAhead.initInput('inputFieldID');

} );

The listName / groupName pair identifies the list of suggestion in the database. If you omit the groupName parameter the list will be shared by everyone. If you set the groupName to something unique, like a username or userid, you’ll have a distinct list per user (or group, whatever…).

The second line binds the object to an input field. As a reference, here’s the HTML markup for the input field. Note that the value of the id attribute is the one used in the binding above.
<input type="text" id="inputFieldID" name="..." ... />

The Type-Ahead object can be binded to only one input at a time, but it can be reassigned to a different field easily and quickly, for instance when the field gets the focus:

document.getElementById('inputFieldID').onfocus = function() {
typeAhead.initInput('inputFieldID') };
document.getElementById('anotherFieldID').onfocus = function() {
typeAhead.initInput('anotherFieldID') };

Database Structure

The ‘USER’ column has been added to the SUGGESTIONS table since the last version . Here’s the SQL statement for MySQL:

CREATE TABLE `SUGGESTIONS` (
`ID` int(11) NOT NULL uto_increment,
`FIELD_ID` varchar(100) NOT NULL default '',
`VALUE` varchar(255) NOT NULL default '',
`USER` varchar(255) NOT NULL default '',
`FREQUENCY` int(11) NOT NULL default '1',
PRIMARY KEY (`ID`))
TYPE=MyISAM AUTO_INCREMENT=1 ;

You’ll need to create a database with this table and change the connection settings in the PHP file (database name, username and password).

Browsers Supported

The type-ahead functionality works with IE5.5+, Safari 1.2+, Gecko-based browsers (Firefox, Mozilla, Netscape 7+, … ) and Opera 7+(*).

(*) Prior to Opera 8.1, user inputs are not recorded in the database.

Note: Comments are now closed. Please visit the discussion board if you want to discuss the subject of this post. Thank you.

Technorati Tags: ,

Client-side data management with XML (part 3: Opera’s DOM 3 Load & Save)

On most recent browsers, you can use a non-rendered DOM Document to load, hold and modify the data of your web application. So far, I’ve been using the Sarissa wrapper to get a simple, unified syntax that works for Gecko-based browsers (Firefox..) and Internet Explorer. Unfortunately Opera and Safari were left out.

Today I’ll show how you can extend support to Opera by using its implementation of the DOM 3 Load & Save specifications.

In short, here’s what we’re trying to do:

  1. Instantiate a non-rendered DOM Document.
  2. Populate the document with XML data loaded asynchronously from the server.
  3. Use regular DOM Scripting to store and update application data in the document.

The code


if (document.implementation && document.implementation.hasFeature && document.implementation.hasFeature('LS', '3.0')) {
var domDoc = null; // will hold our XML
var parser = document.implementation.createLSParser(document.implementation.MODE_ASYNCHRONOUS,null);
parser.addEventListener("load", loadHandler , false);
try {
parser.parseURI('/time-tracker/srvc-tracker.php?taskId=0');
} catch (e) {
alert('error:'+e.code);
}
function loadHandler(e) {
domDoc = e.newDocument;
}
}

The ‘Load & Save’ parser (LSParser) instantiates the DOM Document and takes care of the asynchronous load. The document can be retrieved, once the loading is complete, in the event’s ‘newDocument’ property.

At this point you can use any DOM Scripting method to access and modify the data. See my previous post for more details.

If you need to serialize the document back to a string, use:

var serializer = document.implementation.createLSSerializer();
var xmlstring = serializer.writeToString(domDoc);
alert(xmlstring);

Note: Code samples for a synchronous load can be found on molily’s site (in german) and on Grauw’s web spot.

Technorati Tags: , ,

Ajax Makeover : Client-side data management with XML (part 1)

As I stated before, to build an application that is as responsive as traditional desktop applications, we need to cut down on server accesses. With that objective in mind, we are going to build an Ajax engine that can maintain a complete representation of the state of the Time Tracking application. The engine will process user inputs, apply changes and repaint the interface without any server access (be it a page load or a XMLHttpRequest).

Let’s clarify a bit what the application is supposed to do:

  1. Maintain a list of user-defined tasks.
  2. Keep track of the time spent since the task was started (ie. created in the application).
  3. Allow the user to ‘freeze’ a task that he has temporarily put aside.
  4. Allow the user to leave the application with running or frozen timers and come back later.
  5. Report the time spend on each task.

Loading XML Data

The state of our application will be maintained in a XML document, resident in the browser’s memory. That XML document will be populated with content loaded from the server when the application starts.

Client-Side XML Data Manipulation Diagram

For a new user with an empty task list, the XML is really simple:

<?xml version="1.0"?>
<tasklist name="" >
</tasklist>

<tasklist> is our root element. The ‘name’ attribute will be used as a title for the page.

The Ajax engine, which is nothing more than a well designed javascript, loads that XML using this code:

  var xmlTaskList = Sarissa.getDomDocument();
  xmlTaskList.onreadystatechange = loadHandler;
  xmlTaskList.load("/time-tracker/xml/tmpl_tasklist.xml");

  function loadHandler() {
    if(xmlTaskList.readyState == 4) {
       // ... paint user interface ...
    }
  }

The Sarissa object is a wrapper that offers a unified cross-browser syntax for everything XML. It’s a convenient low-level tool, not an application framework.

The Sarissa.getDomDocument method creates a new instance of a DOM document. The exact same kind of document that holds the representation of a web page, but this one is not rendered and can conveniently hold any type of XML data.

Finally, the Document Object Model offers all kind of methods to manipulate the document. We’ll be using extensively appendChild, removeChild, getAttribute, setAttribute and to a lesser extent createElement and createCDataSection, but this will covered in the second part of this article.

The next article of the Ajax Makeover series will cover the interface rendering using XSLT. That’s when the benefit of client-side XML will reveal itself, so don’t miss it :-)

Technorati Tags: , , ,

Ajax Makeover: Adding a Smart Type Ahead

[Jump to the demonstration page]

If you are like me, you probably have a hard time doing a realistic estimate
of the time you spend on any given task. That’s why Time Tracking
applications were invented… to help you manage and plan your projects
better. Unfortunately, their very existence collides with the way people
like to think and to work. Freely. Without the constraints of a ticking
clock. Making a web application that is actually helpful is going to
be quite a challenge for this makeover.

The first aspect of time tracking application I’m going to cover
is the task input. Chances are, there is a somewhat small set of tasks
that are part of your daily routine: deleting spams, writing status reports,
attending meetings, debugging code, etc… If you were able to make
an exhaustive list, you could populate a <select> field and that
would be your task input.
Of course (or hopefully), your job is not that predictable, and there are
lots of other way for you to spend your time at work. To describe these tasks,
you need a free text input.

The point of the type-ahead functionality (or auto-complete) is to combine
these two needs (predefined list and free text) in one field. The type-ahead
becomes ’smart’ when it learns from previous inputs and other
users and guesses your intent after just a couple keystrokes.

Before going into technical details, you may want to take a look at the demonstration
page.

A Basic Type-Ahead

The functionality core is similar to what Nicholas
C. Zakas presented at webreference.com
. Here is a simplified outline
of the javascript implementation. Cross-browser considerations and advanced
unobtrusive techniques are intentionally
left out
here, so be sure
to check out the final
implementation
for a complete view of the code.

Creating an array of suggestions

var suggestions = ["deleting spam","attending meeting", "writing status report", "debugging code"];

Getting a reference on the task input field

var taskInput = document.getElementById('taskInput');

Capturing the key press event

taskInput.onkeyup = function (evt) {
if(evt.keyCode == 8 ) return; // no type-ahead on backspace

Finding a match in the suggestions array

for (var i=0; i < suggestions.length && suggestions[i].toLowerCase().indexOf(taskInput.value.toLowerCase()) != 0; i++);
if(suggestions[i]) {

Replacing the user input with the suggestion

var userInputLength = self.inputElement.value.length;
taskInput.value= suggestions[i];

And finally, selecting the added text

taskInput.setSelectionRange(userInputLength,suggestions[i].length);
}
}

Adding the Ajax Touch

Querying a suggestion service

We are going to load the suggestion array using an asynchronous call to
a ’suggestion service’ hosted on the server. The idea is to keep the
array short and accurate by querying the server whenever we fall short of
suggestions matching the user’s input.

if (!suggestionsArray[i]) {
var HTTPReq = new XMLHttpRequest();
HTTPReq.onreadystatechange = function() {
suggestions = HTTPReq.responseText.split("&");
}
HTTPReq.open("GET", typeAheadServiceURL + "?inputid="+encodeURIComponent("taskInput")+"&inputtxt="+encodeURIComponent(taskInput.value),true);
HTTPReq.send(null);
}

We are passing two values to the suggestion service: the name of the input
field, and the user input. The service responds with a list of suggestions
(separated by the ampersand ‘&’ character).

Feeding the suggestion service

Now, we want our suggestion service to learn from our user input, so whenever
the form is submitted we do a second call to the service, this time
using the ‘POST’ method.

var HTTPReqPost = new XMLHttpRequest();
HTTPReqPost.onreadystatechange = self.populateSuggestions;
HTTPReqPost.open("POST", self.typeAheadServiceURL, true);
HTTPReqPost.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
HTTPReqPost.send("inputid="+encodeURIComponent('taskInput')+"&inputtxt="+encodeURIComponent(taskInput.value));

 

The Suggestion Service will be presented in the next article. In the meantime
you can play around with the demo page and share your thoughts.

Update: The Type-Ahead functionality now includes a drop-down list of suggestions (’google-suggest’-type).

Update (7/29/2005): A newer version of the script is available.

Ajax Makeover : Time Tracking Application (Introduction)

UPDATE (7/5/05): The Time-Tracker application is now live: www.formassembly.com/time-tracker

Thanks to the readers who suggested ideas for the Ajax Makeover. I eventually chose to go for a simple Time Tracking tool, as you may find in any good project management application. This application will serve as a support for demonstrating and discussing good practices for web application development. I hope you will enjoy it.

The code of the time tracking application is (will be) provided for educational purpose. Unless otherwise specified, it cannot be reused or published without the author (that would be me) consent.

The specifications are as follow:

  • Single-screen application,
  • Input hours by task and project,
  • Add, edit or delete entries,
  • Type-Ahead feature on project and task input fields,
  • Auto-save.

If you think of additional specifications that could be useful, leave a comment.

Ajax Makeover Roadmap

I’ll kick off on Wednesday with an ajax-driven type ahead functionality (a.k.a. auto-complete or auto-suggest).

Technorati Tags: , ,

Ajax Makeover Casting Call

I’d like to hear your suggestions for a short and simple web application that could use a Ajax makeover.

The makeover will be the centerpiece for my next articles on web application developement.

One reader previously suggested a quizz builder, but that’s a bit too close to the form builder. Any other idea?

Next Page »

You are currently browsing the archives for the Ajax category.

Search the Blog Archive

 

The Form Assembly blog is powered by WordPress ~ Entries (RSS) and Comments (RSS).