Drupal AJAX callback only getting called once - ajax

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.

Related

drupal ajax call from click of a link in form

How do I submit a form using AJAX when clicking a link in Drupal?
function search_form($form,$form_state) {
$form['support_email'] = array(
'#theme' => 'link',
'#text' => '',
'#ajax' =>array(
'callback' => 'ajax_change',
'wrapper' => 'email-hidden',
'method' => 'replace',
'click' => 'true',
),
);
}
This is the form link I have inside a form. On clicking the link, I want the AJAX callback function ajax_change to be called, which does not seem to be happening.
The forms api reference for the #ajax functionality says that it is "Used by: button, checkbox, checkboxes, image button, password, radio, radios, select, submit, tableselect, textarea, text_format, textfield". Link is not in the list and thus won't work. The #ajax functionality makes Drupal perform an AJAX call when the specified form element changes. Since a link doesn't change, it is logical that it doesnt work.
The Asaf (ajax submit for any form) module may be able to help you to achieve what you want to do.
Also, it appears you are trying to use this AJAX to hide an element. The Forms API has a states functionality that makes it easy to conditionally show/hide elements. Read this for more info about states.
The #ajax callback is used to make a form that dynamically changes itself.
In Drupal it's not possible (without using third party modules) to use link tag as an AJAX triggering element. The alternative solution may be to create a hidden button element with AJAX functionality, and make link trigger click event on that hidden button.
Here is form function:
function mymodule__form($form, $form_state) {
// JS file contains the code for triggering click event on e hidden button.
$form['#attached']['js'][] =
drupal_get_path('module', 'mymodule') . '/js/mymodule.behaviors.js';
// Our link element.
$form['link_mymodule'] = array(
'#type' => 'link',
'#name' => 'link_mymodule',
'#title' => t('Perform mymodule logic'),
'#href' => current_path(),
'#options' => array(
'attributes' => array(
'class' => array('mymodule_ajax'),
),
),
);
// Hidden AJAX enabled submit button.
$form['mymodule_ajax_submit'] = array(
'#type' => 'button',
'#value' => 'AJAX submit',
'#name' => 'mymodule_ajax_submit',
// You may need this to disable validation.
'#limit_validation_errors' => array(),
'#ajax' => array(
'callback' => '_mymodule__form__pager_callback',
'event' => 'click',
),
'#attributes' => array(
// Class "element-hidden" will hide the button.
'class' => array('element-hidden', 'mymodule_ajax_submit'),
),
);
// Some element for tests.
$form['random_thing'] = array(
'#type' => 'markup',
'#markup' => rand(1, 10000),
// Wrapper is needed since AJAX will use it as a container for incoming data.
'#prefix' => '<div class="ajax_wrapper">',
'#suffix' => '</div>',
);
return $form;
}
You will also need callback function for replacing old data with new one since you are using AJAX:
function _mymodule__form__pager_callback($form, &$form_state) {
return array(
'#type' => 'ajax',
'#commands' => array(
ajax_command_replace('.ajax_wrapper', trim(render($form['random_thing']))),
),
);
}
Also you will have to attach click event to your link, which will trigger click event on a hidden button. That's what stored in /js/mymodule.behaviors.js file.
(function ($, Drupal) {
Drupal.behaviors.mymodule = {
attach: function (context, settings) {
// Since behaviors will be executed every times AJAX is called, it's better to use $.once() method even if you
// are going to use "context" argument. That is needed to be sure that event is attached only once.
$('.mymodule_ajax', context).once('mymodule_ajax', function () {
// Bind click event to out link.
$(this).click(function (e) {
// Prevent browser to follow the link.
e.preventDefault();
// Perform click triggering.
$('input.mymodule_ajax_submit').click();
});
});
}
}
}(jQuery, Drupal));

Is there a way to speed up ajax calls in Drupal forms

I've built a form in Drupal 7 using form API and ajax calls. A typical form item looks like this:
$form['wrapper']['step1']['currency'] = array(
'#type' => 'radios',
'#options' => array(
'USD' => t('USD'),
'GBP' => t('GBP'),
'EUR' => t('EUR'),
),
'#default_value' => (!empty($form_state['values']['currency'])) ? $form_state['values']['currency'] : 'USD',
'#title' => t('Choose Currency'),
'#required' => TRUE,
'#ajax' => array(
'callback' => 'ajax_step1',
'wrapper' => 'step1-wrapper',
'method' => 'replace',
'effect' => 'fade',
'speed' => 'fast',
),
);
Everything is working as should but even when the ajax call just rebuilds a small part of the form it takes couple of seconds (the throbber is working overtime :).
Is this normal?
Is there a way to speed this (keeping things the Drupal way)?
When the ajax call is made, it literally rebuilds the entire form and returns only an aspect of it. For instance, in your function ajax_step1, you are probably calling to return a certain element from the form, and display it in the step1-wrapper div/wrapper element.
If you want to speed up the form return, you need to optimize the form builder itself. Which means, you might need to rewrite the form.

CakePHP 2.1 - Callback functions for JsHelper / AJAX

Using the JsHelper for CakePHP 2.x, for each callback function, is it possible to have more than one selector being subject to various effects. For example, I am using:
echo $this->Js->submit('thumbs-up-green.jpg', array(
'id' => 'thumbs-up-green',
'before' => $this->Js->get('#thumbs-down-red')->effect('fadeOut'),
'success' => $this->Js->get('#thumbs-down-gray')->effect('fadeIn')
));
Let's say I want to apply an effect on #thumbs-down-gray in the before callback function as well (in addition to the effect on #thumbs-down-red which I currently have), what is the syntax for that? I have been searching around but documentation is limited for the JsHelper.
Additionally a simpler question, the JsHelper submit button / form seems to perform a line-break even if CSS display:none; is active. How do I get rid of that line-break?
I haven't found a very neat JsHelper solution, I don't think I can chain on additional elements and actions.
So I think for more complex callback functions the solution is to execute a pre-loaded function like:
'before' => 'someFunction();',
But if you just want to for example fadeOut multiple elements in one go then you could write it like:
'before' => $this->Js->get('#thumbs-down-red, #other-element')->effect('fadeOut'),
You have to set the "before"-action in a variable before the actual submit-call. Like this:
$before = $this->Js->get('#skadetyp_form')->effect('fadeOut', array('buffer' => false));
$before .= '$(\'#notice\').append(\'<div class="notice">Sending..<br/>' . $this->Html->image('load_bar.gif', array('alt' => 'Loading..')) . '</div>\')';
$complete = $this->Js->get('#notice div')->effect('fadeOut', array('buffer' => false));
$complete .= '$(\'#notice\').append(\'<div class="success">Succssfully seny!</div>\')';
echo $this->Js->submit('Send!',
array(
'update'=>'#skadetyp_form',
'complete' => $complete,
'before' => $before,
'error' => $error,
'async' => true,
'method' => 'post',
'dataExpression'=>true,
'data'=> $this->Js->serializeForm(
array(
'isForm' => true,
'inline' => true
)
)
)
);

Drupal 7 + JS code through the AJAX

I had created several modules extending the UI elements of the FormAPI. This works fine when I render forms in a normal way (not AJAX). But if the rendered form is delivered though the AJAX request, JS script simply inserts the code via the $.html function. So I can't access elements of the document by their ID from the script evaluated by the $.html.
Is there any solution to pass JavaScript code through the Form/Ajax API?
I think jQuery("#new-id") (not $(..)) should take the newest dom elements.
You can use either the 'ajax' property of form api
$form['myitem'] = array(
'#type'=>'textfield',
'#ajax' => array(
'callback' => 'my_callback',
'wrapper' => 'new-id',
'method' => 'html',
'effect' => 'fade',
),
);
$form['myitem2'] = array(
'#type'=>'markup',
'#value'=>"<div id='new-id'> I'm #new-id </div>"
);
function my_callback(&$form, &$form_state){
$commands = array();
$commands[] = ajax_command_invoke('#new-id', 'html', array('<div id="new-id2">#new.id2 here!</div>'));
$commands[] = ajax_command_invoke('#new-id2', 'addClass', array('error'));
return array('#type' => 'ajax', '#commands' => $commands);
}
D7 ajax_commands:
http://api.drupal.org/api/drupal/includes--ajax.inc/group/ajax_commands/7
hope this help

Why won't Drupal AHAH forms work within a block?

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.

Resources