Ajax

Ajax is a Web mechanism that allows Javascript running in a Web page to communicate back with its server to request additional information, which can then be used to update parts of the page dynamically. It's very handy for producing nice interactive effects such as autocompletion, status updates, and selective exposure of data. Fiz contains facilities that make it easy to use Ajax.

Overview: the server is in charge

In Fiz Ajax requests are handled in three phases. In the first phase, Javascript code running in the browser initiates a request. To do this it calls a Fiz method and specifies a URL (which can include query values) plus optional additional data. This launches an Ajax request back to the server. The request is asynchronous: the initiating Javascript code does not wait for the request to complete, and it does not handle the response. Most likely the request was initiated from within an event handler; the event handler completes and the browser goes about its business while the request is carried out in the background.

In the second phase, the request arrives at the server, where it is dispatched to a Java method. The code on the server has complete control over the handling of the request: it can choose among several actions such as modifying the HTML of the page or executing arbitrary Javascript code in the browser. Fiz packages up these actions into a response message that is returned to the browser.

In the third phase the browser receives the response from Fiz. The response is dispatched automatically to Fiz Javascript code in the browser, and the Fiz code carries out the actions that were specified by the server. The Javascript code that initiated the request is not involved at this point: the actions specified by the server carried out in the background by Fiz library code.

Thus the browser initiates Ajax requests but the server completely controls the outcomes. This approach moves most of the complexity to the server side, keeps the Javascript code on the browser as simple as possible, and gives the server maximum flexibility.

Initiating Ajax requests

There are three ways to arrange for an Ajax request to occur. The first two approaches are used in Java code on the server, and the third approach can be used in Javascript code you write for the browser:

  • Use a built-in Ajax feature in one of the Java components. For example, the Button, Link, and TabSection classes provide an ajaxUrl option: if you specify this option then an Ajax request will be initiated to the specified URL when the user clicks on the asasociated component.
  • Call Ajax.invoke in your Java code. This method will return Javascript code to invoke an Ajax request, which you can then incorporate into a Web page that you are constructing. Here's a simple example:
    Template.append(cr.getHtml().getBody(), "<div onclick="@1">...</div>",
            Ajax.invoke(cr, "ajaxDoClick"));
    
    This will create a <div> element with an onclick handler that invokes an Ajax request to the URL ajaxDoClick. This approach has the advantage of hiding the Javascript details of making an Ajax request; Ajax.invoke takes care of that for you.
  • In some cases you will need to invoke an Ajax request but you can't call Ajax.invoke to generate the requesting code for you. This can happen, for example, if you are writing a static Javascript library package that needs to make Ajax requests. In this case, you can invoke an Ajax request from Javascript by creating a new Fiz.Ajax object, like this:
    new Fiz.Ajax("/blog/ajaxUpdate?userId=4123"); 
    
    This will initiate an Ajax request to the URL /blog/ajaxUpdate. If you'd like to provide additional data (separate from the query values), you can invoke the constructor like this:
    new Fiz.Ajax({url: "/blog/ajaxUpdate?userId=4123",
          data: {first: 200, last: 499}}); 
    
    In this form the argument to the constructor is a hash; the url entry contains the URL to invoke, and the data entry is another object containing name-value pairs that are transmitted to the server along with the request. In both of these forms the request is initiated by the constructor and will be carried out in the background after the constructor returns.

URLs and dispatching: "ajax" prefix

Ajax URLs are dispatched to a Java method on the server just like other URLs; see URL Dispatching for details. For example, the URL blog/ajaxUpdate will be dispatched to a method named ajaxUpdate in an Interactor class named BlogInteractor. The method names for Ajax requests must always start with ajax. Fiz handles Ajax requests differently than other requests (for example, the response is packaged differently, and errors are reported differently), and Fiz depends on the ajax prefix to indicate that it should use Ajax handling.

Handling Ajax requests

The Java code that handles an Ajax request typically does not generate HTML like a normal page request would (any such HTML will be ignored by Fiz). Instead, the handler code requests one or more of the following actions to be performed by the browser:

  • Replace the innerHTML of a DOM element with a new value. This action is requested by invoking the methods ClientRequest.updateElement and/or ClientRequest.updateSections.
  • Redirect the browser so that it replaces its current page with a new URL. This action is requested by invoking ClientRequest.redirect.
  • Execute Javascript code. This action is requested with the method ClientRequest.evalJavascript.
  • Display information in the bulletin area at the top of the Web page. This action is requested with the methods ClientRequest.addMessageToBulletin and ClientRequest.addErrorsToBulletin.

Any number of actions may be requested; they will be executed in the order requested. None of the actions execute until the Ajax handler returns, at which point information about all of the actions is returned to the browser for execution.

When an Ajax handler is invoked the main dataset will contain the incoming data for the request, including both query values from the URL and additional data passed to the Fiz.Ajax constructor.

Passing complex data to/from the browser

If you need to pass a large amount of structured data from the server to the browser, the easiest way to do this is using Datasets. The Dataset method toJavascript will generate a Javascript object literal that represents the contents of the Dataset: nested datasets will be represented using nested objects or arrays of objects. For example:

Dataset d = new Dataset("name", "Alice",
        "phone", new Dataset("type", "home", "number", "650-496-8104"),
        "phone", new Dataset("type", "cell", "number", "408-733-9925"));
d.toJavascript()

--> {name: "Alice", phone: [{type: "home", number: "650-496-8104"},{type: "cell", number: "408-733-9925"}]}

Anything that can be represented as a Dataset can be converted to a Javascript literal in this way. You can then include this literal in Javascript code like this:

cr.evalJavascript(Template.expandRaw("var x = @1;", d.toJavascript()));
cr.evalJavascript(Template.expandRaw("x.setData(@1);", d.toJavascript()));

Note: you should use Template.expandRaw, not Template.expandJs for including the literal in Javascript code: toJavascript has already escaped any special characters in the data values, and its result uses Javascript characters such as " that must not be escaped.

You can also send structured data from the browser to the server in Ajax requests. To do this, pass an Object literal structure to the Fiz.Ajax constructor. This will result in the creation of a nested Dataset in the main dataset on the server. For example, the following code will create a nested Dataset in the main dataset named person with the same structure as Dataset d from above:

new Ajax({url: "blog/ajaxUpdate", data: {person: {
        name: "Alice",
        phone: [
            {type: "home", number: "650-496-8104"},
            {type: "cell", number: "408-733-9925"}
        ]}}});

Managing state: page properties

In many cases your Ajax handler will need access to state that was created when the page was first rendered. One approach is to include this state in the page and return it as data with the Ajax request. However, this approach is insecure: there is no guarantee that the browser will return the same state that you included in the page (an evil client can fabricate arbitrary Ajax requests). For example, if you passed the name of a database table this way, an evil client could generate and Ajax request with a different table name, potentially leaking sensitive information or corrupting your database.

The best way to handle state needed by the Ajax handlers is to use the Fiz page property mechanism. You can use the method ClientRequest.setPageProperty to store information when you first render a page; then, any subsequent Ajax request for that page can retrieve the saved information using ClientRequest.getPageProperty.

Error handling

If an error occurs during an Ajax request it can be handled in any of three ways:

  • The handler can generate a UserError exception with a message for the user. Fiz will catch this exception and arrange for the message to be displayed in the bulletin. To do this, Fiz will use the bulletin.userError entry in the styles dataset as an HTML template, expand that template with a dataset whose message entry contains the message from the exception, and add the resulting HTML to the bulletin.
  • The handler can generate some other uncaught exception. If this happens, Fiz treats it as an internal error in the application. Fiz will add a message to the bulletin in the same way as for UserError exceptions, except that it will use the template named bulletin.uncaughtAjax to generate HTML for the bulletin.
  • The handler can detect the error and handle it itself, using any mechanism it chooses. For example, the handler can invoke cr.addMessageToBulletin to add a message to the bulletin, or it can log information to the server's log.