Thanks, Darvanen. Your lead helped quite a bit.
After reading through a few similar issues and reading up on ajax_commands, I think I found a solution. The trick was adding an #ajax property to my submit button that invoked a callback that checked for any form errors. If any form errors were found, the form on the page needed to be replaced by the original target div - the one that was initially replaced by the current form - which then needed to be replaced by the rebuilt form. A similar process had to be followed to get the $messages presented on the current page. I'm not sure this is the right way (or the 'Drupal' way), but it seems to be working. If anyone has any suggestions, I'd be grateful to hear them!
Here's the module file I was playing around with:
/**
* Implements hook_init()
*/
function sandbox_ajax_init() {
drupal_add_library('system', 'drupal.ajax');
module_load_include('inc', 'node', 'node.pages');
}
function sandbox_ajax_menu() {
$items = array();
$items['ajax/sandbox/get'] = array(
'title' => 'Ajax Callback',
'page callback' => 'sandbox_ajax_ajax_callback',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'delivery callback' => 'ajax_deliver',
);
$items['sandbox-ajax'] = array(
'title' => 'Sandbox Ajax Page',
'page callback' => 'sandbox_ajax_page_callback',
'access arguments' => array('access content'),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
/**
* Implements hook_form_alter()
*/
function sandbox_ajax_form_alter(&$form, &$form_state, $form_id) {
if ($form['#form_id'] == 'sandbox_file_node_form') {
$form['actions']['submit']['#ajax'] = array(
'callback' => 'sandbox_ajax_form_submit',
'wrapper' => 'sandbox-ajax-wrapper',
'method' => 'replace',
);
}
}
/**
* KEY TO DISPLAYING FORM ERRORS
*
* Custom Ajax Submit Callback
*/
function sandbox_ajax_form_submit($form, &$form_state) {
$commands = array();
$form_selector = '#sandbox-file-node-form';
if (form_get_errors()) {
// Remove old messages
$commands[] = ajax_command_remove('#messages-wrap');
// Replace form with the target div for the rebuilt form
$commands[] = ajax_command_replace($form_selector, '<div id="sandbox-ajax-wrapper"></div>');
// Add error messages to page
$commands[] = ajax_command_after('#main-content', '<div id="messages-wrap">' . theme('status_messages') . '</div>');
// Replace the div with the rebuilt form - errors included :D
$commands[] = ajax_command_replace('#sandbox-ajax-wrapper', drupal_render($form));
} else {
// If we get here, form validation passed. HOWEVER, if error messages were
// presented before, they're still hanging out in the messages div. Out with
// the old, in with the new.
$commands[] = ajax_command_remove('#messages-wrap');
$commands[] = ajax_command_after('#main-content', '<div id="messages-wrap">' . theme('status_messages') . '</div>');
// Finally, replace the form wrapper with an empty div with the original
// target id.
$commands[] = ajax_command_replace($form_selector, '<div id="sandbox-ajax-wrapper"></div>');
}
// Return the page.
$page = array('#type' => 'ajax', '#commands' => $commands);
return $page;
}
/**
* Page callback
*/
function sandbox_ajax_page_callback() {
$wrapper = array();
$wrapper['#attached']['js'] = array(
drupal_get_path('module', 'sandbox_ajax') . '/js/sandbox_ajax.js',
);
$wrapper['container'] = array(
'#type' => 'container',
'#attributes' => array(
'id' => 'sandbox-ajax-wrapper',
'title' => t('Click to load'),
),
);
$wrapper['container']['content'] = array(
'#markup' => t('Loading...'),
);
return $wrapper;
}
/**
* Ajax Form Callback
*/
function sandbox_ajax_ajax_callback() {
$commands = array();
$output = sandbox_ajax_prep_form('sandbox_file');
$selector = '#sandbox-ajax-wrapper';
$html = drupal_render($output);
$commands[] = ajax_command_replace($selector, $html);
$page = array('#type' => 'ajax', '#commands' => $commands);
return $page;
}
/**
* Form Prep Helper
*/
function sandbox_ajax_prep_form($type) {
global $user;
module_load_include('inc', 'node', 'node.pages');
$node = (object) array(
'uid' => $user->uid,
'name' => (isset($user->name) ? $user->name : ''),
'type' => $type,
'language' => LANGUAGE_NONE
);
$output = drupal_get_form($type . '_node_form', $node);
return $output;
}
And here's the JS file:
(function ($) {
Drupal.behaviors.sandbox_ajax = {
attach: function (context, settings) {
$('#sandbox-ajax-wrapper').once('sandbox-ajax-wrapper', function() {
var base = $(this).attr('id');
var argument = $(this).attr('argument');
var element_settings = {
url: Drupal.settings.basePath + 'ajax/sandbox/get',
event: 'click',
progress: {
type: 'throbber'
}
};
Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
$(this).click();
});
}
}
}(jQuery));
Similar issues for reference:
#ajax['wrapper'] removed by validation errors
AJAX commands and drupal messages