The issue:
In Drupal 7's Form API, when using #AJAX to refresh fields, error messages from validation are not displayed until the entire page is refreshed. I see the field I refreshed highlighted in the error state, but the user does not see the associated message until it's too late (they've reloaded the page, or gone to another page).
I started to manually process the error stack in this manor: Drupal.org -- Filter out specific errors during validation, but I have a complex form, and a small budget of time to complete this task. There must be some way to refresh the stack & display the messages to the user without processing everything manually.
Note:
I'm using multi-commands with a callback, so utilizing this is an option for me.
$commands[] = ajax_command_replace("#my_wrapper", render($form['test']['field_a']));
$commands[] = ajax_command_replace("#another_wrapper", render($form['test']['field_b']));
return array('#type' => 'ajax', '#commands' => $commands);
Thoughts?
Solution:
Apparently, when you use the multi-callback approach, Drupal does not refresh messages for you. You can do this manually, as follows:
// Remove the old messages div, clearing existing messages.
$commands[] = ajax_command_remove('#messages');
// Append a new messages div with our latest errors and messages.
$commands[] = ajax_command_after('#header-region', '<div id="messages">' . theme('status_messages') . '</div>');
Simply add this to any callback commands[] array you have, and you're good to go.
Thanks to Drupal.org -- AJAX commands and Drupal Messages for the right direction!
thanks atomox! just a fuller example for those not onto multi comands yet:
$form['ld']['letterdrop_allocations_submit'] = array(
'#type' => 'submit',
'#value' => 'Letterdrop allocations update',
// '#name' => 'Letterdrop allocations update',
'#name' => 'agc_letterdrop_submit',
'#ajax' => array(
'callback' => 'agc_ems_form_letterdrop_submit_ajax',
),
);
...
function agc_ems_form_letterdrop_submit_ajax($form, &$form_state) {
drupal_set_message('here i am world', 'ok');
$commands = array();
$commands[] = ajax_command_replace('#messages',
'<div id="messages">' . theme('status_messages') . '</div>');
$commands[] = ajax_command_alert('submitted via handler');
return array(
'#type' => 'ajax',
'#commands' => $commands);
}
Also is a bit more concise and removes the assumptions that messages sit below the header region:
$commands[] = ajax_command_replace('#messages', '<div id="messages">' . theme('status_messages') . '</div>');
Related
Im in drupal 7, i need to make a select with many options, depends of the option taken, in a textarea will be loaded several values in a string.
After hours of test i come here for help.
Im working on a basic page:
function ajax_load_all_admins($form, &$form_state) {
$form = array();
$form['changethis'] = array(
'#type' => 'select',
'#options' => array(
'' => '',
'1' => 'Cargar todos los admins'
),
'#ajax' => array(
'event' => 'change',
'callback' => 'ajax_load_all_admins_callback',
'wrapper' => 'listaCorreos-div'
)
);
$form['listaCorreos'] = array(
'#type' => 'textarea',
'#prefix' => '<div id="listaCorreos-div">',
'#suffix' => '</div>'
);
if (!empty($form_state['values']['changethis'])) {
$payments_list = db_query('QUERY WORKING WELL');
$value = '';
foreach ($payments_list as $payment) {
$value .= $payment->admin . ',';
}
trim($value, ',');
$form['listaCorreos']['#default_value'] = $value;
}
return $form;
}
function ajax_load_all_admins_callback($form, $form_state) {
return $form['listaCorreos'];
}
$form = drupal_get_form('ajax_load_all_admins');
print drupal_render($form);
The Ajax call is working but i only recibe:
0: {command:settings, settings:{basePath:/, pathPrefix:,…}, merge:true}
No other one position.
I think it can be for the drupal_render, but dont know why?
Thanks in advice.
since the ajax itself works;
Looks to me like the db_query isn't working well and/or returning unexpected results.
My advice: You should be able to debug. i.e. setting a breakpoint and stepping into your code line by line
I do it with netbeans & XDEBUG
This should gives you a great edge solving this & upcoming similar problems, as you'll be able to monitor your variables & the execution tree of your code
Best of luck.
--edit-- This should be totally in the comment section, but ... new here , cant comment at the moment.. apologies.
I was doing this ajax functions in a simple view, created to make an specific form.
I move all the logic to a new module, instead of simple view and now is working.
I take a look to the ajax examples of "examples module" and test if they works on a simple view,like my code, dont works.
I think for any reason, drupal ajax only works if the render is not manually, like i was doing.
Thanks.
Ok, this might have a very simple answer but for the life of me my I can not seem to find an answer! I am signing up users from a text into a lights out box (SimpleModal) which AJAX loads a new page for an admin to sign up a user to a selected client list.
This all works without any issues at all, so long as the model checks are correct. I have two checks, one makes sure the username is unique and that the password has at lest 8 characters, code below. But when one or both of these checks are not meet, then the user is taken to the AJAX URL and the 'message' is then displayed. This is not what I need I need it to be taken back to the lights out box or set these messages as flash error message to be printed on to the screen.
Or should I remove these checks from the modal and get JQuery to check them instead?
Any ideas?
All help very welcome.....
Model Code ::
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('isUnique'),
'message' => 'Sorry but a unique username is required'
)
),
'password' => array(
'required' => array(
'rule' => array('minLength', '8'),
'message' => 'Sorry but a password of 8 characters or more is required'
)
) ... more check follow but these are the issues....
CTP file ::
$this->layout = 'ajax';
$AddUserForm = $this->Form->create('User', array('url' => '/ADD-USER-URL-HERE'));
$AddUserForm .= $this->Form->input('username');
$AddUserForm .= $this->Form->input('password');
$AddUserForm .= $this->Form->input('role', array('options' => array('admin' => 'Admin', 'user' => 'User')));
$AddUserForm .= $this->Form->input('data_id', array('options' => array($data), 'empty'=>true));
$AddUserForm .= $this->Form->end(__('SAVE NEW USER'));
echo $AddUserForm;
In your ctp file
$('form').on('submit', function(e) {
e.preventDefault();
$.post($(this).attr('action'), $(this).serialize(), function(res) {
$('form').replaceWith(res);
})
})
can be used for validating the data using the above script,
I think this will help you for submitting the form via ajax and you can also display the error messages using the response coming from the ajax.
Hope this will help you.
I'm trying to make a form in Drupal, so more than one element can be added to a form. For example, a page might contain data for an event, then the event might have multiple dates. So I have a form that looks like:
/**
* Implements hook_form_alter().
*/
function addextra_form_alter(&$form, &$form_state) {
if ($form['#form_id'] == 'test_content_node_form' ) {
$form['elements_table'] = array(
'#theme' => 'table',
'#title' => 'Elements already added',
'#header' => array('Item', 'Remove'),
'#empty' => 'No elements',
'#prefix' => '<div id="elements-table">',
'#suffix' => '</div>',
);
$form['add_elements'] = array(
'#title' => 'Add another element',
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['add_elements']['add_content'] = array(
'#type' => 'textfield',
'#description' => t('Add an element to the table'),
'#title' => t('Add another item'),
'#size' => '12',
'#maxlength' => '60',
'#prefix' => '<div id="addextra_content">',
'#suffix' => '</div>',
);
$form['add_elements']['add_another_btn'] = array(
'#type' => 'button',
'#name' => 'add_another',
'#button_type' => 'submit',
'#executes_submit_callback' => FALSE,
'#value' => 'Add another',
'#ajax' => array(
'callback' => 'addextra_element_to_table',
),
);
}
}
When 'add_another_btn' gets clicked, it will run the ajax callback 'addextra_element_to_table.
That callback is:
function addextra_element_to_table(&$form, &$form_state) {
$form['elements_table']['#rows'][] = array($form_state['values']['add_content'], l('Remove Item', '#'));
drupal_add_js(drupal_get_path('module', 'addextra') . '/addextra.js');
return array(
'#type' => 'ajax',
'#commands' => array(
ajax_command_replace('#elements-table', render($form['elements_table'])),
),
);
}
The js file called replaces the val of the input field to ''
(function ($) {
$('#edit-add-content').val('');
})(jQuery);
But this callback only gets called one time. I believe this is because the behaviour has to be attached again once it's been called. Sorry for my ignorance - I'm not sure how to achieve this. Can anyone help me out? It would be much appreciated. Thanks in advance.
The problem is that render(), which basically just calls drupal_render() , does not process the #ajax element, it's simply ignored. You might want to try and pass the element through ajax_pre_render_element() before the call to render().
That said, I personally don't have good experience with trying to reuse Drupal functions outside their normal calling sequence, especially not with forms. I prefer to stick to the very top level functions, such as drupal_get_form. I have followed those functions many times in my debugger, and they do a bunch of things in a precise order that is hard to get a hold of when you want to reuse pieces of that.
To alter a form with AJAX callbacks, I would always prefer one of two strategies:
In the callback, adjust the content of the $form argument and do return $form. This requires that you set #ajax['wrapper'] to the id (the value of the id attribute in the markup, as used for CSS) of the form on the originating element (the button in your case). Then Drupal gets to do its shpiel with the whole form, and the browser replaces the whole thing. Drupal takes care of preserving values already entered etc.
Alternatively, you can have the callback return a set of commands that do very specific modifications on the DOM. In your case that would be commands which create and append new rows. Keep in mind that with ajax_command_invoke(), you have the entire jQuery arsenal at your disposal.
From those two strategies, I usually prefer the second one, because it seems more elegant for little tweaks. However, if you want to build on Drupal's rendering, or if you have more massive changes to the form, you should use the first one.
As a side note, did you know that there is drupal.stackexchange.com? Drupal can be quite peculiar, and on that site, you'll find more experts on the specifics.
I have installed drupal 6, added some cck fields in one content type. Added two select box fields.
I am taking the selected value of parent select box and as per that selection passing relates options to next select box field using Ajax. (e.g Country -> State. When user selects country I want to pass state values into next select box.)
But when I am submitting the form it gives the following error:
"An illegal choice has been detected. Please contact the site administrator."
I don't know why it is not taking the ajaxified select box values while saving the node.
Does somebody has the solution on it. Is there any solution to handle this dynamic select options in Drupal.
Thanks in advance.
The same thing I'm working on drupal 7 and its work for me. Below is the code. Hope this help you. What I have done is on selection of car model car variant will change and the data save in the table.
function add_offer_form($form, $formstate) {
$form['add_offer_new_car_model'] = array(
'#type' => 'select',
'#required' => TRUE,
'#options' => $car_model,
'#ajax' => array(
'effect' => 'fade',
'progress' => array('type' => 'none'),
'callback' => 'variant_callback',
'wrapper' => 'replace_variant',
),
);
// Combo box to select new car variant
$form['add_offer_new_car_variant'] = array(
'#type' => 'select',
'#options' => array(),
// The prefix/suffix provide the div that we're replacing, named by #ajax['wrapper'] above.
'#prefix' => '<div id="replace_variant">',
'#suffix' => '</div>',
);
// An AJAX request calls the form builder function for every change.
// We can change how we build the form based on $form_state.
if (!empty($formstate['values']['add_offer_new_car_model'])) {
$model_id = $formstate['values']['add_offer_new_car_model'];
$rows = array();
$result = db_query("SELECT id, variant_name from {va_car_variant} where car_model_id in ($model_id,1) order by variant_name");
while ($data = $result->fetchObject()) {
$id = $data->id;
$rows[$id] = $data->variant_name;
}
$form['add_offer_new_car_variant']['#options'] = $rows;
}
}
////////////////////////////////////////////////////////
///////// FUNCTION FOR AJAX CALL BACK
function variant_callback($form, &$form_state) {
return $form['add_offer_new_car_variant'];
}
I'm trying to get an AJAX-submitted (AHAH) form working to display in a block on the sidebar. For testing purposes, I'm using an example module called "Poof" from the Pro Drupal Development book: http://books.google.com/books?id=VmZrdGuBZCMC&lpg=PA269&ots=cnHiYG6kXn&dq=pro%20drupal%20development%20poof&pg=PA269#v=onepage&q=pro%20drupal%20development%20poof&f=false
The only thing I've added to the example so far is an implementation of hook_block, which looks like this:
function poof_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks[0]['info'] = t('poof');
return $blocks;
case 'view':
$block['content'] = drupal_get_form('poof_form');
return $block;
}
}
The AJAX module works fine when displaying on its own page (mydrupalsite.com/poof) but when I call the form with module_invoke('poof', 'block' ...) in a template file, the form submits as normal (sans AJAX) and refreshes the page.
I can't find a definitive answer for why this happens, though a found something tangentially related that suggests that maybe AHAH doesn't work within blocks. If that's so, why? Or better yet, what's a work-around. Do I have to put the on its own blank page and bring it in with an iframe? That sounds unnecessarily messy.
UPDATED:
Here's more code for reference (again, it's from the Pro Drupal book)
function poof_form() {
$form['target'] = array(
'#type' => 'markup',
'#prefix' => '<div id="target">',
'#value' => t('Click the button below.'),
'#suffix' => '</div>',
);
$form['submit'] = array(
'#type' => 'button',
'#value' => t('Click Me'),
'#submit'=>false,
'#ahah' => array(
'event' => 'click',
'path' => 'poof/message_js',
'wrapper' => 'target',
'effect' => 'fade',
),
);
return $form;
}
function poof_message_js() {
$output = t('POOF!');
drupal_json(array('status' => TRUE, 'data' => $output));
}
Try adding
$blocks[0]['cache'] = BLOCK_NO_CACHE;
to your hook_block implementation.
Rendering a form with ahah causes a call to drupal_add_js to add the ahah javascript, but while the output of the block is cached, the javascript that gets added to the page doesn't.