A Guide to Implementing a Custom Modal Dialog Visual Design

March 25th, 2009 by Martti Tamm

The modal dialog design that comes with Aranea release, is derived from Modalbox. Unsurprisingly, this design might not fit to all the needs out there. Therefore, it is important to be able to switch the implementation used. Previously, one had to write pages of code to do that. Now, since Aranea 1.2.1 release, it has been drastically simplified, and here’s how to do it.

The API

First, let’s take a look at the API of Aranea.ModalBox JavaScript code. Below, the code that is commonly appropriate to be rewritten is marked with bold text. Note that the API code has been altered to resemble to JavaDoc, though invalid in pure JavaScript.

/**
 * Returns the URL for submitting to in overlay mode.
 */
String Aranea.ModalBox.getRequestURL()

// The main function called either to start the overlay.
void Aranea.ModalBox.show(options)

// This function is called by events to update the
// content of overlay dialog. Params contains the
// data to be used in the request.
void Aranea.ModalBox.update(params)

// A simple implementation of request with optional
// callbacks for both success and failure. When no
// callbacks are defined, no content will be updated.
// However, default error reaction is that overlay will
// be closed.
void Aranea.ModalBox.doRequest(options, success, failure)

// Utility method for checking whether the response
// indicates that the overlay mode should be closed.
boolean Aranea.ModalBox.isCloseOverlay(content)

// This method is called each time when the request for
// overlay content ends. The content can be used to
// check whether the overlay mode should be closed.
void Aranea.ModalBox.afterLoad(content)

// Gets executed after update region response has been
// processed completely.
// Note that without update regions, this is not called.
void Aranea.ModalBox.afterUpdateRegionResponseProcessing(systemForm)

// Closes the overlay mode (only visually).
void Aranea.ModalBox.close()

// Reloads the page. This is usually called when the
// overlay mode terminates. Reloading will refresh the
// entire page content and status.
void Aranea.ModalBox.reloadPage(options)

// Closes the overlay mode. It is meant to be used, if
// you don't have a "Close" button, but you still want
// to close the overlay in some other way (e.g. with
// an ESC-key). Once the overlay is closed, a page
// reload is also done.
void Aranea.ModalBox.closeWithAjax(eventId, eventTarget, eventParam)

Therefore, the API provides some useful functions that we may need sometimes, e.g. isCloseOverlay() uses the returned content from the server to let us know whether overlay should be closed, and closeWithAjax() allows one to send an event to an event handler on the server side to close the overlay. After that the overlay is closed as usually (useful for key listeners waiting for the escape key to close overlay, and when the form has no close button).

However, as you may have noticed, the most important methods are

// This is called when the overlay is opened for the
// first time after page load.
void Aranea.ModalBox.show(options)

// This is called after when a request should be done to
// the server. The params contains serialized form data.
void Aranea.ModalBox.update(params)

// It is optional to implement: executed when a region
// of overlay is updated. You can do resizing here.
void Aranea.ModalBox.afterUpdateRegionResponseProcessing(systemForm)

// Closes the overlay visually. You should also remove
// content from the HTML page.
void Aranea.ModalBox.close()

A custom code

Next, you can start implementing them. Fortunately, you can use doRequest() method to simplify handling of AJAX requests by using callbacks. Here’s an example:

Object.extend(Aranea.ModalBox, {                                  (1)
    show: function(options) {                                     (2)
        this.Options = options;
        this.doRequest(this.Options, function(content, options) { (3)
            $('modal-content').update(content);
            showModalDialog(options.size);
        });
    },

    update: function(options) {                                   (4)
        Object.extend(this.Options, options);
        this.doRequest(this.Options, function(content, options) {
            $('modal-content').update(content);
            resizeModalDialog(options.size);
        });
    },

    afterUpdateRegionResponseProcessing: function() {             (5)
        resizeModalDialog(this.Options.size);
    },

    close: function() {                                           (6)
        $('modal-content').update();
        hideDialog();
    }
});

Let’s analyse the important logic of this code.

  1. Object.extend() comes from the Prototype library and copies the properties of given code to the object specified as the first parameter. Therefore, our code is copied to Aranea.ModalBox.
  2. In the show() method we store the options in Aranea.ModalBox: this.Options = options. Note that this.Options is not defined anywhere else before, but this statement is perfectly legal in JavaScript. The options that come as a parameter were specified to the OverlayContext, and are finally passed to the show() method that stores them in a property of Aranea.ModalBox.
  3. The doRequest() method of Aranea.ModalBox is used for AJAX, and a callback is provided as its parameter. The callback expects the returned content and the same options that were provided to doRequest(). These options can be used to specify custom arguments like the second line of the callback does. As shown, the callback is responsible for doing something useful with the returned data. Note that it is possible to provide another callback as the third parameter for error handling (in case the request fails).
  4. The update() method does similar stuff: updates the content in an existing element with the request data and resizes the modal dialog window. However, also note an important code on the first line of the update() method: it copies the properties of the parameter to the this.Options property, and then the latter is used in doRequest(). Why is this important? Because the parameter of update() called param does not contain as much data as the show() method gets. The param contains only serialized form data, which can be used to update the local property this.Options. Therefore, the size property is only available the local property. In addition, the latter demonstrates how you can use additional parameters passed to the OverlayContext.
  5. The afterUpdateRegionResponseProcessing() method demonstrates how you can resize the dialog after a region has been updated.
  6. The close() method shows how the element containing the overlay content is emptied the overlay is visually closed.

What else to consider?

It is also important that when you write a JSP for overaly root (e.g. overlayRoot.jsp) then declare the form as following:

<ui:systemForm
        id="overlaySystemForm"
        method="post"
        styleClass="aranea-overlay">
    ...
</ui:systemForm>

Note that the style class of the form is aranea-overlay. It is important that you preserve it!

In addition, don’t put your element, which will contain the overlay content, inside a (system) form! Your overlay form will be lost in that case. If you are using Firebug, you can test in overlay mode, whether the form has the right style class with this:

AraneaPage.findSystemForm().hasClassName('aranea-overlay')

If the code above returns true then overlay system form has been successfully found.

The Modalbox dependencies?

If you have decided to start using your own modal dialog design then most likely you don’t need the same dependency files. If you don’t use Modalbox, you can omit these JavaScript and CSS files:

  • css/modalbox/modalbox.css
  • js/scriptaculous/builder.js
  • js/modalbox/modalbox.js

There is some speed to gain from here! :)

Leave a Reply