Mercenary module development: AJAX Form Messages

Benjamin Melançon's picture
Submitted by Benjamin Melançon on 2010, September 29 - 10:27
  • Developing a module for hire can be a lot of fun, especially with a good client, because the requirements are figured out and laid out for us.

    Although the request was to build off of an earlier module (made public but not released on Drupal.org), the goals expanded enough that it makes sense to start fresh— the client, who has Drupal 6 needs, even OKd a Drupal 7 first approach.

    We make a new module in a working copy of Drupal and immediately start a git repository for the module.

    If the modules directory doesn't already exist in sites/default, we'd have to make it.

    Command-line steps


    mkdir -p sites/default/modules/formmsgs
    cd sites/default/modules/formmsgs/
    git init

    We start our .info file by stealing the one from Unique fields module, because we expect to be working closely with, or at the very least learning a lot (also known as stealing) from, this module.

    (Note: The author has aliased 'v' to 'gvim'-- there is no v command, so readers are advised to do the same alias or use the command vi, gvim, emacs, or any other chosen editor instead.)

    Command-line steps


    cp ../../../all/modules/unique_field/unique_field.info formmsgs.info
    v formmsgs.info


    ; $Id$
    name = AJAX form messages
    description = "[formmsg] Provides immediate, in-form validation requirements, especially to ensure that a node's author, language, taxonomy terms, or other fields are unique."
    core = 7.x
    files[] = formmsg.module

    That worked so well, let's do the same with the .module file.

    Command-line steps


    cp ../../../all/modules/unique_field/unique_field.module formmsgs.module
    v formmsgs.module

    We keep saying we're going to provide an API, but what does that mean? It means a lot more – and in some cases, it means something different – than simply having the user interface in a different module. After all, presenting form messages is a matter for the user interface and pretty fundamental to what our module does.

    One way to look at being an API means our module doesn't do anything. It just is.

    Our module handles interaction with the form, based solely on what is told to do by other modules.

    This means we must distill what's needed to show a message down to its essential parts. We can't assume that our module has any way to just know when or how it's supposed to do something.

    Here are some of the questions our module must ask, then, of modules that would work with it. This will tell us what what data our module must store and process, and how it interacts with these other modules– in short, just what kind of API it needs.

    What triggers the error?
    What does the message say?
    Does the trigger also run when the form is submitted?

    What must trigger the error is a function akin to a validation function. Indeed, it would be quite inconvenient if our module did not make it possible to reuse validation functions as error message evaluation functions. (Drupal's form validation API is probably not clean enough to automatically create AJAX messages from validation functions, unfortunately.)

    However, validation functions frequently take an entire form, and we're talking about AJAX on keystrokes here (only when leaving a form field, but still it can happen multiple times a form, and it needs to be fast). Efficiency is important. What information has to be sent to the evaluation function?

    The whole form is definitely easiest, but maybe we can make it so it either expects a form object (which could include only the parts we care about, but still be processed by any normal, non-over-zealous validate function, or a single specific element,

    http://api.drupal.org/api/function/date_validate/7), that despite its parameter name, is clearly expecting a form element, not an entire form.

    Each evaluation/message combination needs to provide:

    • form ID (or an array of form IDs)
    • optional: further conditional logic on whether to apply the evaluation and message (all content types use the same node add form, for instance).
    • form element location (or an array of form element locations)
    • evaluation function to run
    • whether the evaluation function receives a form-array -like object, or a single element
    • message to provide on failure
    • is this a warning, that allows the form to submit, or an error, that will be added to the form's validation routine? [or an error which is already applied to the form validation routine– maybe to start, ignore this and run it twice.
    • optional: message to provide on success.

    And our module will take care of the rest.

    Form element location, as can be deduced even from its awkward way it is named here, is a bit messy. We have to decide if we try to find the form elements even if they've been moved inside a fieldset grouping or if we require our modules to say exactly where it is in the form array. The more helpful way (done by http://api.drupal.org/api/function/form_set_error/7 as used by http://api.drupal.org/api/function/form_error/7 ) is slower.

    It's been quite a while since we initialized our Git repository, and we haven't even committed anything yet. That's more OK when starting out— and this way, we've been able to remove evidence of our crime of stealing our initial code from the Unique Fields module before putting anything in the permanent record.

    Command-line steps


    git add .
    git commit -m "Initial commit of stub code for AJAX form messages module."

  • Explanation

  • better wording of no v command
  • Book element

  • How to