O'Reilly logo

Ajax Design Patterns by Michael Mahemoff

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 12. Code Generation and Reuse

THERE ARE TWO GENERAL PROGRAMMING PATTERNS IN THIS CHAPTER. SERVER-SIDE CODE GENERATION IS one technique people are using to avoid working directly with HTML and JavaScript. Cross-Browser Component is a component that’s been developed for portability across the major browsers.

Server-Side Code Generation

⊙⊙ AutoGenerated, CodeGen, Framework, Library

Server-Side Code Generation
Figure 12-1. Server-Side Code Generation

Developer Story

Dave’s creating a web app with a trendy new server-side framework, ZeroWeb. The framework lets him write everything in Java as if he’s writing a standalone GUI application. It’s capable of using that code to generate a conventional edition of the application, An Ajax edition, a desktop edition, and—for an extra $99.95—a smartphone app too.

Problem

How do you avoid using HTML and JavaScript?

Forces

  • HTML and JavaScript are the only code recognized by standard browser—any Ajax App must be represented in a combination of these formats.

  • Ajax is increasing the complexity of HTML and JavaScript. Whereas JavaScript was previously used for simple tasks like navigation support or form validation, it’s now driving the interaction in many applications.

  • Most Ajax Apps also have a substantial backend, which is rarely implemented in JavaScript.

  • It’s difficult to completely separate the roles of browser-side developer and server-side developer. In fact, it’s often more desirable to have the same developer working on a vertical slice of functionality; i.e., contributing a little to all tiers in order to deliver a new requirement. Thus, the same developer is often expected to work across tiers, where the environment differs significantly.

  • Server-side development has certain advantages over JavaScript development. In some cases, there’s better support for code editing, refactoring, testing, and so on.

  • Sometimes, you want different user interfaces for the same application—a non-Ajax version or a desktop version, for example.

Solution

Automatically generate HTML and JavaScript from server-side code. You write code in a standard server-side language like Java and it’s converted to HTML and JavaScript. Depending on the framework, you might not need to code directly in HTML or JavaScript at all. Since you’re unlikely to create such a framework yourself, this pattern relies on a certain category of publicly available Ajax framework (see Appendix A).

In the case of all code being auto-generated, the code is something like a conventional desktop application. Widgets and event handlers are defined, and the browser generates the appropriate HTML, along with XMLHttpRequest Calls (Chapter 6) and general JavaScript handling. As well, the framework will often generate Ajax Stubs to deal with incoming calls and pass them to appropriate application code.

In other frameworks, the approach is more mixed. The framework might let you create a Slider with just a single tag, for example. But in the tag, you’ll specify a JavaScript function to be called whenever the slider value changes.

There are several arguments for Server-Side Code Generation:

  • It may be more productive, as the server-side language and framework may be more expressive and also because the framework might remove redundancy across the tiers (such as a data structure having a JavaScript representation as well as a server-side representation).

  • Developers don’t have to know HTML and JavaScript; they can work in the server-side language and environment they’re most familiar with.

  • The framework can help with graceful degradation. In other words, it can free the developer of worrying about browser portability, JavaScript, or cookies being turned off, or XMLHttpRequest Calls timing out.

  • The framework can, in theory, deliver multiple platforms simultaneously. A single code base can be used to generate a web application (Ajaxian or not), standalone desktop application, and maybe even a mobile application.

However, code generation does comes at a cost to usability. Look at it this way: anything you could generate from the server, a skilled developer could also hand code. The reverse is not true; given enough time and talent, it’s always possible to build a custom solution that’s superior in terms of usability. So, to be worthwhile, the framework must add enough value in terms of productivity and support for non-experts, to be compensated for the drop in usability. Whether this can be achieved really depends on two things: how much users will gain from a custom-built application, and how much of a boost the framework will give you. On the second point, consider a few counter-arguments to the benefits above:

  • The first point, about developers not having to learn HTML and JavaScript, has traditionally held the most weight. Server-side developers in the past produced minimalist satisficing interfaces rather than optimal ones, allowing them to focus on the server side. It’s not uncommon to see advice against anything relatively complex being performed on the server side. But as Ajax becomes more prominent, this argument becomes less compelling. As discussed in Fat Client (Chapter 13), JavaScript is undergoing a renaissance—developers are learning more about it, tools and libraries are evolving, and patterns are emerging. All this reduces the argument for isolating developers from HTML and JavaScript.

  • A server-side framework may well be able to handle graceful degradation and browser versions transparently. But to the extent that’s possible in the server, a script can equally achieve the same effect in the browser, as discussed later in Cross-Browser Component (see later in this chapter). Portability is really a lame-duck argument for using server-side frameworks.

  • The dream of transforming a single code base into a variety of different user-interface styles has been around for a while. Certainly, many modern UI toolkits are cross-platform, running on different operating systems from the same code base. However, can a single code base lead to both a web app and a desktop application? It’s possible in theory and in proof-of-concept demos, but in practice, users expect applications to follow the conventions of the platform and that’s difficult to achieve with a single generation.

An interesting variant of Server-Side Code Generation is “Browser-Side Code Generation”; the Backbase framework (http://backbase.com) is a case in point. With Backbase, the server outputs a specialized markup language, BXML, and the browser converts it into HTML, CSS, and JavaScript.

I expect the full-featured frameworks to become particularly prominent on intranet applications, where new functionality is often more critical than usability, and users can be trained to work around problems. Hand-coded HTML and JavaScript is more likely for public dot-coms where alternatives are abundant and the pressure of competition means it’s critical to optimize the user interface. In those environments, there is still some role for Server-Side Code Generation, but mainly as a helper for very specific tasks and with some flexibility, rather than as the overarching architectural style.

Real-World Examples

These examples describe frameworks that support this pattern in different ways.

Echo2

Echo2 (http://www.nextapp.com/products/echo2/) is an open source framework by NextApp, which takes the “whole hog” approach and generates all HTML and JavaScript from Java source code. An online demo (http://demo.nextapp.com/InteractiveTest/ia) shows how Echo2’s user-interface widget classes manifest themselves in the browser (Figure 12-2).

Echo2 demo
Figure 12-2. Echo2 demo

Ruby On Rails framework

Ruby On Rails (http://rubyonrails.com) is a web framework that embeds Ruby inside web pages. That in itself is not new, but Rails offers built-in helper classes to speed up page construction. They often lead to the Scriptaculous library being invoked, but the programmer doesn’t use it directly. For example, you can issue an XMLHttpRequest Call followed by Display Morphing, using the following embedded Ruby call (http://www.onlamp.com/pub/a/onlamp/2005/06/09/rails_ajax.html):

  <%= link_to_remote( "click here",
                  :update => "time_div",
                  :url =>{ :action => :say_when }) %>

Backbase

Backbase (http://backbase.com/) is a commercial framework that extends standard XHTML with Backbase-specific tags (BXML). A Backbase client-side framework translates the BXML to HTML, CSS, and JavaScript, making it easy to implement many standard Ajax features. So there’s a widget tag that looks like this:

<b:panel>

and another tag for various visual effects:

<s:fxstyle>

There are several innovative demos available from the Backbase homepage (http://www.backbase.com/#home/pulldown/demos.xml[2]) (Figure 12-3). On the server, Backbase offers programming interfaces for both .NET and JSF and offers visual development tools for Visual Studio .NET as well. As with the other tools here, the developer doesn’t have to deal with HTML or JavaScript, but the framework also lets people customize the generation for specialized browser appearances and behavior. Note that, as mentioned earlier in the "Solution,” Backbase is actually a kind of client-side, rather than server-side, code generation.

Backbase Travel demo
Figure 12-3. Backbase Travel demo

AjaxTags library

AjaxTags (http://ajaxtags.sourceforge.net/) is a collection of JSP tags to ease development of Ajax interaction. There’s an <autocomplete> tag for Suggestion and an <ajax:update> field to perform a Display Morphing (Chapter 5) with the response of an XMLHttpRequest Call (Chapter 6).

Comfort.ASP library

Comfort.ASP (http://www.daniel-zeiss.de/ComfortASP/) takes an unusual approach by automatically adding Web Remoting (Chapter 6) to a conventional, non-Ajax application. By instrumenting the HTML, the framework can intercept standard form submissions and transform them into XMLHttpRequest Calls. By monitoring server output, Comfort’s server-side script can respond to these calls by sending back only deltas—the difference between the current HTML and the desired HTML.

Code Example: Echo2 Number Guess

Echo2 (http://www.nextapp.com/products/echo2/) provides full source for a tutorial application, Number Guess (http://www.nextapp.com/products/echo2/doc/tutorial/application.html). The user guesses a number between 1 and 100, the application tells them if it’s higher or lower, the user tries again, and so on. Note that the code is available, but there’s no online demo at present.

An Echo2 application is run as a Java servlet extending Echo2’s WebContainerServlet. The superclass takes care of most interaction; the concrete servlet must do just one thing: declare an ApplicationInstance. In Echo2, an ApplicationInstance object is the application state for a given user:

  /**
    * Guess-a-number Tutorial Application Servlet Implementation.
    */
  public class NumberGuessServlet extends WebContainerServlet {

    /**
      * @see nextapp.echo2.webcontainer.WebContainerServlet#newApplicationInstance( )
      */
    public ApplicationInstance newApplicationInstance( ) {
      return new NumberGuessApp( );
    }

  }

The main application object initializes a new window and controls two top-level user-interface elements: a GamePane on startup, which is where the main action takes place, and a CongratulationsPane upon victory:[*]

  public class NumberGuessApp extends ApplicationInstance {
      private Window mainWindow;
      public Window init( ) {
          mainWindow = new Window( );
          mainWindow.setTitle("Echo2 Guess-A-Number");
          startNewGame( );
          return mainWindow;
      }
      void startNewGame( ) {
          ...
          mainWindow.setContent(new GamePane( ));
      }
      void congratulate(int numberOfTries) {
          mainWindow.setContent(new CongratulationsPane(numberOfTries));
      }
  }
  class GamePane extends ContentPane implements ActionListener {
    ...
  }
  class CongratulationsPane extends ContentPane implements ActionListener {
    ...
  }

The GamePane is built up by adding subelements and registering events. It acts as an ActionListener for the buttons it contains; hence, there’s an actionPerformed event, which encapsulates the guess evaluation logic. The code illustrates that a framework like Echo2 paves the way for a style of web programming that’s almost identical to traditional desktop coding.

  class GamePane extends ContentPane implements ActionListener {

    private int randomNumber = ((int) Math.floor(Math.random( ) * 100)) + 1;
    private int lowerBound = 1;
    private int upperBound = 100;
    private int numberOfTries = 0;
    private TextField guessEntryField;
    private Label statusLabel = new Label( );
    private Label countLabel = new Label("You have made no guesses.");
    private Label promptLabel= new Label("Guess a number between 1 and 100: ");
    private int guess;

    GamePane( ) {
        super( );

        Column layoutColumn = new Column( );
        layoutColumn.setCellSpacing(new Extent(10));
        add(layoutColumn);
        ...
        Button submitButton = new Button("Submit Your Guess");
        submitButton.setActionCommand("submit guess");
        submitButton.setForeground(Color.BLACK);
        submitButton.setBackground(Color.GREEN);
        submitButton.setWidth(new Extent(200));
        submitButton.addActionListener(this);
        layoutColumn.add(submitButton);
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand( ).equals("new game")) {
            ((NumberGuessApp) ApplicationInstance.getActive()).startNewGame( );
        } else if (e.getActionCommand( ).equals("submit guess")) {
            ++numberOfTries;
            ...
            if (guess == randomNumber) {
                ((NumberGuessApp) ApplicationInstance.getActive( ))
                     .congratulate(numberOfTries);
            } else if (guess < 1 || guess > 100) {
                statusLabel.setText("Your guess, " + guess  + " was not between
                     1 and 100.");
            } else if (guess < randomNumber) {
              ...
            }
            ...
        }
    }
  }

Alternatives

Browser-Side Templating

Browser-Side Templating (Chapter 11) is a form of Client-Side Code Generation, an alternative to Server-Side Code Generation.

Related Patterns

HTML Message

Server-Side Code Generation produces HTML responses, so is technically one form of HTML Message (Chapter 9). It’s also a form of JavaScript Response, the variant described in On-Demand JavaScript (Chapter 6). However, the emphasis in those patterns is on directly created HTML and JavaScript, rather than delegation to a generation framework.

Cross-Browser Component

⊙⊙⊙ Agnostic, Common, Component, CrossBrowser, Independent, Portable

Cross-Browser Component
Figure 12-4. Cross-Browser Component

Developer Story

Dave has developed a cross-browser sprite engine, allowing users to easily drag a sprite around the screen. The library provides a browser-independent interface, taking into account browser incompatibilities regarding detection of mouse position and positioning of icons. When a new drag-and-drop requirement comes along, programmers can code to Dave’s library in blissful ignorance of the underlying browser incompatibilities.

Problem

How can you manage cross-browser incompatibilities?

Forces

  • A major selling point of Ajax is portability—the ability to run an application on any platform with a modern browser.

  • There are numerous browsers on the market, and while IE still dominates, the market is becoming increasingly fragmented. IE currently has around 85-90 percent share on average web sites, but within certain user groups, Mozilla is actually far more popular. A recent survey of AjaxPatterns.org traffic showed 47.5 percent hits from Firefox, 30.1 percent from IE, and 2.7 percent from Safari.

  • Browsers vary from each other in subtle and not-so-subtle ways. Sometimes, a browser simply doesn’t support certain functionality. Other times, two browsers offer the same functionality, but it must be accessed in different ways. While the W3C (http://www.w3c.org) establishes standards for browsers, not all are followed.

  • When creating a web program, you want to focus on business and application logic, without being distracted by idiosyncrasies of underlying browsers. Cross-browser compatibility is a cross-cutting concern, best dealt with as a separate task at a separate time.

Solution

Create Cross-Browser Components, allowing programmers to reuse them without regard for browser idiosyncrasies. A Cross-Browser Component abstracts away from underlying browser issues, offering a single API that can be used on any supported browser.

Following are a couple of examples of cross-browser incompatibilities. For simplicity, only the two most popular browsers are discussed: IE and Firefox. Other browsers are also important, and not surprisingly, introduce further incompatibility issues. The following are two examples:

XMLHttpRequest

XMLHttpRequest, a critical Ajax component, is constructed in a browser-specific way, as established in XMLHttpRequest Call (Chapter 6). Most browsers include a specific XMLHttpRequest class, but with IE prior to version 7, it’s created as an ActiveX component instead. Furthermore, there are some subtle behavior differences. For example, XMLHttpRequest on Opera lagged behind in terms of support for features like authentication (http://www.scss.com.au/family/andrew/webdesign/xmlhttprequest/). An even greater problem is that many older browsers simply don’t support this object, even though similar functionality can nonetheless be achieved using IFrames.

opacity

opacity—or transparency —is becoming important in Ajax applications, as discussed in Popup (Chapter 15) and in One-Second Spotlight (Chapter 16). However, portability is poor (http://www.sitepoint.com/newsletter/viewissue.php?id=3&issue=102&format=html). Recent versions of Firefox, Opera, and Safari allow for the CSS3 opacity style, older versions use their own different names, and IE uses a completely different approach based on a DirectX filter.

Any application using the features above must take into account cross-browser portability. Business and application logic is complex enough, without tangling it further with if-then portability statements. A better solution, and a common approach in the general area of portability, is to isolate cross-browser concerns.

Some browsers are available in multiple operating systems—Firefox, for example, runs in MS-Windows, Apple, Linux, and many other operating systems. It would be nice to assume a given browser was identical across all operating systems, but that’s not the case. Some aspects of browsers rely on OS-specific features, which is where further incompatibilities arise. For example, IE’s drag-and-drop capability not only depends on which version of IE is being used, but also on whether it’s running on Apple or MS-Windows.

As a simple example, many libraries exist to make XMLHttpRequest calls portable. These libraries are examples of Cross-Browser Components, because the user doesn’t have to worry about the browser being used. Most of these libraries internally use another form of Cross-Browser Component—a factory function that retrieves a suitable XMLHttpRequest object.

ajaxCaller (http://ajaxify.com/run/testAjaxCaller/), for example, contains the following factory function:

  createXMLHttpRequest: function( ) {
    try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
    try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
    try { return new XMLHttpRequest( ); } catch(e) {}
    alert("XMLHttpRequest not supported");
    return null;
  }

The function allows any code to create a new XMLHttpRequest object without worrying about browser specifics. The returned object is a Cross-Browser Component.

Note that this pattern applies as much to server-side code as to browser-side JavaScript. Whatever HTML comes from the server also needs to be portable. For that reason, you can also create server-side components that will generate portable HTML. So if you create custom JSP tags for your project, the JSP coder should be able to use them without having to make any browser-specific checks.

Decisions

What browser-specific criteria is used to create the Cross-Browser Component?

What criteria do you use to decide on a creation strategy? There are, broadly speaking, two approaches:

Version-dependent

Behavior is based on the browser version.

Feature-dependent

Behavior is based on checking for the existence of specific features—specifically whether a property or method exists.

The XMLHttpRequest example in the earlier "Solution" is an example of feature-driven behavior. Before trying to create a new window.XMLHttpRequest, we check if window.XMLHttpRequest exists. An equivalent version-dependent check would do something like this:

  /* Version-Dependent Check (Unadvisable!!!) */
  return determineIfCurrentBrowserIsIE( ) ?
    new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest( );

In the above example, a function determines if the current browser is IE. Based on that knowledge, the right mechanism can be used to create an XMLHttpRequest object. How might the version be determined? There are a few ways. First, the navigator object reports directly on this information. Useful properties include (http://www.javascriptkit.com/jsref/navigator.shtml):

navigator.appName

Browser name; e.g. “Netscape”

navigator.appVersion

Browser version; e.g., “5.0 (X11; en-US)”

navigator.UserAgent

Detailed browser and platform details; e.g., “Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5”

Ironically, the navigator object’s behavior is itself somewhat inconsistent across versions, so you need to handle strings like the userAgent differently. If you go this route, consider a browser detection library like TechPatterns’ JavaScript Browser & OS Detection (http://techpatterns.com/downloads/javascript_browser_detection.php) or Webreference’s JavaScript Browser Sniffer (http://webreference.com/tools/browser/javascript.html). Portability aside, the problem with direct interrogation is that the browser sometimes tells fibs—Opera, for example, lets users manually choose which browser to self-identify as, because some sites refuse to support browsers reporting as Opera.

An alternative way to detect browser version is to rely on a distinctive feature. For example, many scripts use document.all as a simple check for Internet Explorer (http://weblogs.asp.net/jgalloway/archive/2005/08/07/421798.aspx):

  isIE=document.all;

Version-dependent behavior is feasible, quite common, and often recommended as the appropriate solution. One benefit over feature-dependent behavior is that you have only one or two variables to deal with; with feature-dependent behavior, you have to consider many combinations—what if the browser supports X but not Y?

However, there do remain some fundamental problems with any feature-dependent behavior. First, it’s cumbersome to track all the browser versions that support a particular feature. In many cases, you risk ruling out other browsers that may well support the feature. Also, version checks are often used in a binary way: either your browser’s supported and you can run the app, or it’s not supported and you’re out of luck. But if you focus on specific features, you can support progressive degradation: have a basic application that works for all browsers, with certain additional features available only to those browsers that support them.

What if a feature isn’t supported by the current browser?

Sometimes, a browser simply doesn’t support a feature. What does a cross-browser API do when asked for functionality the browser doesn’t provide? There are a couple of options:

Do nothing

Throw an exception or return null to indicate the object couldn’t be created.

Provide a custom version

The browser may not support an object natively, but you might be able to create a custom version from other supported objects.

Real-World Examples

This section contains one example that showcases a particularly interesting type of cross-browser compatibility. For more examples of Cross-Browser Components, refer to Appendix A. Many of the JavaScript libraries mentioned there, like Dojo and Scriptaculous, aim to provide components that work on all major browsers.

HTMLHttpRequest library

Angus Turnbull’s HTMLHttpRequest (http://www.twinhelix.com/javascript/htmlhttprequest/) is a cross-browser library for Web Remoting (Chapter 6). Unlike many of the recent Ajax libraries, it’s able to gracefully degrade to IFrame usage when executed on older browsers. The API’s still the same, so a programmer can access web remoting in the same way, regardless of the browser in question.

Code Example: HTMLHttpRequest Library

HTMLHttpRequest (http://www.twinhelix.com/javascript/htmlhttprequest/) is a cross-browser remoting component. It works by wrapping around a real, concrete remoting object—either an XMLHttpRequest object or an IFrame, only one of which will be set. The construction code below makes extensive use of feature-dependent behavior to decide, at each stage, whether to proceed with a particular strategy. There is also some use of feature-dependent behavior to ensure that Opera and IE5 don’t follow a particular path.

  function HTMLHttpRequest(myName,callback){
    this.myName=myName;
    this.callback=callback;
    this.xmlhttp=null;
    this.iframe=null;
    ...
    if(window.XMLHttpRequest){
      xmlhttp=new XMLHttpRequest( );
      if(xmlhttp.overrideMimeType)
        xmlhttp.overrideMimeType('text/xml')
    }
    if(!xmlhttp) {
     if(   document.createElement && document.documentElement
          && (window.opera||navigator.userAgent.indexOf('MSIE 5.0')==-1)) {
          ...
      } else if (document.body && document.body.insertAdjacentHTML) {
          ...
      }
      ...
    }
    return this;
  }

Related Patterns

Server-Side Code Generation

Server-Side Code Generation (see earlier) is often a convenient way to generate Cross-Browser Components, allowing you to perform all the abstraction logic server side.

On-Demand JavaScript

If you design carefully, you can separate out the implementations for each platform. Use the Lazy Loading technique of On-Demand JavaScript (Chapter 6) to ensure that only code required for the user’s own platform is downloaded.

Metaphor

Using a Cross-Browser Component is like working with a certified professional. The professional may have gained their certification in different ways and might offer different services, but there remains a basic set of services that you can expect to be carried out adequately.

Want to Know More?



[*] All comments are omitted in the code sample.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required