Hi,

I would like to attach a zip file containing two example modules to the Doing AHAH Correctly in Drupal 6 page (http://drupal.org/node/331941). It would be nice if I got a go from some documentation guru and a quick review of my code to make sure it's okay.

AHAH is a confusing topic and it took me the better part of a week to grasp the concepts behind it. The handbook page wasn't very helpful. If you browse through the comments, a lot of questions remain unanswered and some suggestions are downright wrong.

The package contains two modules that demo basic AHAH principles:

A. Button demo module

Demonstrates an implementation of AHAH based on a submit button.

B. Select demo module

Demonstrates an implementation of AHAH based on a select list.

Both modules contain comments which explain what happens through the process.

Package attached to this issue.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

WildKitten’s picture

Great examples. I just have to ask you, do you know how to add those 2 select fields to existing form, in ahah_example_select_form_alter?
Cause when I do this:

  $form = array();
  // Set the cache to true to create a $form_state cache on submit
  $form['#cache'] = TRUE;

I loose all data that have been already in $form.

greg.harvey’s picture

No time today, but will look at these ASAP. Subscribing to remind me to come back later. =)

asmdec’s picture

This code works fine with simple forms, but when you have some more fields or required fields some problems happen.

I have a form with 4 fields, 2 hierarquical selects and 2 required textfields (title and body). When the value of first select changes, the second select doesn't update, instead I receive a message to correct the required fields (title and body).

I think that some validations should not happen during AHAH callbacks. How could I disable this validations?

I remove the theme('status_messages') from JSON return and when the form is posted I receive the unecessary messages.

Sorry for my bad english :P

netsensei’s picture

Well, these are just two textbook examples that show the basic principles behind AHAH.

I suppose you're trying to add AHAH to a node form. Maybe you should check out this handbook page:

http://drupal.org/node/331941

Consider the AHAH cycle it describes. Between steps 4 and 5, you could alter the $form array and set #required on both the title and body fields to FALSE. Maybe that would make it work.

WildKitten’s picture

I just manage to make it (2 select fields in form_alter) with ahah. I'm close to my deadline (with clients), but I'll give my best to post my solution in next week.

asmdec’s picture

I'm using the following code to remove the messages and CSS error styles before drupal_json is called.

form_set_error(NULL, '', TRUE); // remove CSS error styles
drupal_get_messages(); // remove all messages

It works, but if I could skip validations instead of just hide them, would be nice :)

asmdec’s picture

I don't understand how the callback of type_submit button is called on change event of the type select.

Please, anyone could explain?

asmdec’s picture

Reading "Forms API Quickstart Guide" in "Submitting Forms" I figured it out.

1. A submit function is called only if a submit button was present and exists in the $_POST, and validation did not fail.

The data I was posting to my form was not valid (title and/or body required fields are empty) and the flow was not passing to the type_submit submit function breaking the flow, and the animals_kind select wasn't updated.

asmdec’s picture

netsensei,

setting the #required attribute of required fields to FALSE, works very nice.

I think that's a missing functionality to skip validations instead of doing this work in all fields that have some validation rule that can cause this effect, right?

netsensei’s picture

Asmdec,

It's not really a case of "missing" functionality here. AHAH functionality in Drupal provides you with several hooks where you should implement your own code. The problem is that it's not very well documented what you can and can't do.

Altering the #required attribute in the AHAH callback shouldn't pose a security risk (can someone confirm this?) because the data is just copied to the $form_state variable. It's used to repopulate the form when it's re-rendered with the changed elements.

If you would submit your entire form for further processing, you should be getting a validation error when a required field wasn't filled out.

oboskovic,

I saw your form topic but didn't have much time to give you an entire answer. Using form_alter in a seperate module to give existing fields the AHAH attribute would be my strategy too. I would then implement the required submit handlers and the AHAH call to make it all work in this module. You should pay attention to the naming of your functions and the callbacks. It's easy to make a mistake and AHAH is hard to debug.

I saw your comments on http://drupal.org/node/331941#comment-1503260. This code is using the 'wrong' way as Kat Bailey points out. It's changing the form directly in the AHAH call (mymodule_change_flower() function) and that's just, well, plain wrong. The callback is used to get the data into $form_state and call the submit handlers. Changes to the form happen in the mymodule_form rendering form itself. Or in form_alter in your case.

--
Colada | http://www.colada.be

asmdec’s picture

Netsensei,

I appreciate your help and understand your effort to better document AHAH for developers to know exactly what they're doing. Perhaps I don't use the right word when I say "missing" (bad english :), sorry.

I think that this "missing" functionality is (could be) in Form API, in drupal_process_form more exactly. It would receive some parameters that indicates that validation is not needed.

Hierarquical selects (i.e.) don't need to validate the entire data of the form to populate one element. We're thinking only in #required validator, but we have many other validators that need to be disabled as well, like mail validator, user-defined validators, etc.

In the case of not valid data posted to form, could I call form_execute_handlers('submit', $form, $form_state) after step #5 (drupal_process_form call)?

Sorry if my ideas are confused, I'm learning AHAH these days and I don't have very deep knowledge so.

asmdec’s picture

I'm having another kind of bug now, the same as (not)discussed in http://drupal.org/node/371339.

1/ im creating a new node
2/ im using ahah to change a value
3/ i submit the form without filling mandatory fields
4/ so i have a new page telling me "missing fields..." and now with the new node form, the main form "action" is set to the ahah submit url

You know any about this??

greg.harvey’s picture

@asmdec, just do a drupal_get_messages() call after right at the end of your callback function and it will clear out any messages set during the node form validation that don't apply at this point. =)

WildKitten’s picture

I make 2 files for everyone how needs 2 select fields (like country and city, or category and subcategory), in form_alter(). First file shows an ordinary , custom fields added in some content_type by form_alter function.

And second file shows how to change 2 cck fields into 2 select fields with ahah. More specific , it is a workaround for Hierarchical Select fields in some content type. I had to solve this problem fast, and this was the only solution after 10 days of research. It is not nice, but it works.
I made content type with 2 text fields called: field_category and field_subcategory. I made taxonomy Location, that has depth 2. (I mean it has Countries and Cities items). In form_alert function I made 2 dummy fields called: category and subcategory. They are select fields , and they are made with ahah, to replace Hiearachical select fields. I also didn't give any permission for cck fields, it means, admin is the only one who can see it.
The script works like this: you chose category, then ahah display subcategory for that category, and when you click on save/submit, you trigger JS that puts those values in corresponding cck fields field_category and field_subcategory. This way, drupal insert/edit/update all data by himself (you don't need to write code for that). That is all.
JS on the end of second file should be put in page.tpl.php.

I was in hurry so sorry if there are some minor mistakes.

netsensei’s picture

Well I spent an hour or so to fit the code from my select example in a CCK form with hook_form_alter.

I'm faced with the same issues:

* If a required field isn't filled, it complains.
* After an AHAH call, the form action is changed to something else and the data isn't saved in a new node.

I'm going to delve a bit deeper in this and see what I can do to make it work.

--
Colada | http://www.colada.be

asmdec’s picture

The #action attribute is altered by default form element information provided in hook_elements hook on system module:

$type['form'] = array('#method' => 'post', '#action' => request_uri());

request_uri is returning the uri called via AHAH in this case. It happens in drupal_rebuild_form call, wich calls drupal_prepare_form, wich calls _element_info('form'), wich invokes the hook_elements hook and set the #action as the uri requested by AHAH.

I'm setting manually the #action attribute.

$form['#action'] = '/path/to/original/action';
creazion_dev’s picture

Thx. You made my day.

Edward.H’s picture

thanks,You are a star!

cfmcoder’s picture

The example code for select works for Firefox, but fails for IE6 and IE7. When the first select is chosen, the second select does not appear. Any ideas?

himerus’s picture

Title: AHAH done correctly » Some notes on my own implementation

I've noticed a couple things on my own implementation. Some have already been mentioned here.

In order to prevent issues with multiple validations (form validate works once, then the #action url is reset to the callback rather than the correct one, so the #action parameter needs to be manually set using:

  $form['#action'] = base_path() .'/path_to_your/page_callback';

Rather than CSS'ing out the extra submit button, it is more appropriate to use #access = FALSE to avoid it rendering. The callback is still registered, and will be used during submission of the select element

  $form['theme_submit'] = array(
    '#type' => 'submit',
    '#value' => t('Select Theme'),
    '#access'=> FALSE,
    '#submit' => array('delta_add_override_select_theme_submit'),
  );

Also, regarding having the validation callbacks called when the AHAH element is submitted, I only found one clean option.
I've avoided using #required = TRUE, even on the fields I do require. What I have done also in my validate callback for the main form is to check to ensure the appropriate button was used for the callback, in most default cases the #id is edit-submit

function delta_add_override_validate($form, &$form_state) {
  // only valid when we are truly submitting the form, not for AHAH
  if ($form_state['clicked_button']['#id'] == 'edit-submit') {
    $delta = $form_state['values']['delta_override'];
    if (!$delta['name']) {
      form_set_error('name', t('You must create a valid <strong>Override Title</strong> for this override.'));
    }
    if (!$delta['system_name']) {
      form_set_error('system_name', t('You must create a valid <strong>System Name</strong> for this override.'));
    }
    if (!$delta['theme']) {
      form_set_error('theme', t('You need to select a theme for this override to apply to.'));
    }
  }
}

This really is a great piece of documentation. Needless to say the AHAH implementations are confusing at best, and this goes a little way further to a better understanding for myself personally.

I'll say it's not perfect, but it is a BIG step towards a more usable explanation. Thanks for the work!!

himerus’s picture

Title: Some notes on my own implementation » AHAH done correctly

My bad, accidentally reset title, changing back.

TwoD’s picture

I have recently been trying to restructure a module's AHAH handling and wondered why none of my code worked while examples like the ones provided in ahah_example.zip did, especially the one with selects. So, I stepped through the code using a debugger and tried to carefully take notes of what happened, and here are the results:

The select example in the provided ahah_example.zip relies on a workaround for an IE behavior/bug and is easily broken.
The only reason the ahah_example_select_type_submit handler is called instead of none at all is that includes/form.inc has _form_builder_ie_cleanup($form, &$form_state). It is designed to identify the case where IE does not pass information about the clicked button in the POST data, because there was only one button present and the enter key was used to submit the form. This case is very similar to when a changed select was used to trigger an AHAH POST.

_form_builder_ie_cleanup($form, &$form_state) will always set the first button encountered as the 'clicked' button and use its submit handlers if no button has already been identified.

To confirm this, insert a return; at the top of _form_builder_ie_cleanup($form, &$form_state), or make sure it's not called when running the example. No submit handler will run when AHAH is triggered via the first select, so the second select will always be replaced by its default state.

If the Save button is simply moved before the "Submit the type" button in the code, the form will be passed through its submit handler instead and the result is rather confusing...

If more than one #ahah-select is present in the form, and they all use an extra button for their submit handlers, only the first encountered submit handler will ever be run regardless of which select is changed.

Adding #ahah-selects like this via hook_form_alter will not work as intended because the original form's Submit button will be encountered before the button added by the form altering implementation. This is most likely to cause the form values to be saved to the database "behind the user's back" (if they happen to validate).

I have come up with a few workarounds for this, but I have only partially tested them. The first is to actually use the extra button, introduced to hold the #submit handler and just move the #ahah stuff from the select to it. If we really want the select to do stuff when changed, use a JavaScript to simulate a click on the #ahah-button. The 'ajaxSend' and 'ajaxComplete' events from jQuery could be used to disable and re-enable the select so it can't be clicked during a request.
We could also keep the #ahah stuff on the select and use jQuery's 'ajaxSend' event to inject the POST data which would have been created if the extra button had been clicked. The 'ajaxStart' event is global and is sent a reference to the URL and serialized form data so we can listen to it from anywhere and manipulate the data without risking other AJAX functionality being disturbed. Just note that if the form is re-serialized using .formSerialize() from jquery.form.js, one must first enable the select or its value will not be included, then disable it again as it will be automatically re-enabled when the request completes. The disabling/enabling flicker on the elements should be hardly noticeable.

arianek’s picture

Hey, if you see any of this info as useful additions to the current AHAH section of the handbook, please do add it! http://drupal.org/node/348475 you can set the issue status to "needs review" if you've made changes and would like the docs team to review the formatting, etc. Thx!

netsensei’s picture

Yes. I've written the modules a few months ago. In the mean time, I got a chance to delve quite deep into this. The messy execution of submit handlers really is a problem.

A good help is Wim Leers' AHAH helper module. About the problem you describe, I find this in the comments:

  // If the form is being rebuilt due to something else than a pressed button,
  // e.g. a select that was changed, then $_POST['op'] will be empty. As a
  // result, Forms API won't be able to detect any pressed buttons. Eventually
  // it will call _form_builder_ie_cleanup(), which will automatically, yet
  // inappropriately assign the first in the form as the clicked button. The
  // reasoning is that since the form has been submitted, a button surely must
  // have been clicked. This is of course an invalid reasoning in the context
  // of AHAH forms.
  // To work around this, we *always* set $form_state['submitted'] to true,
  // this will prevent _form_builder_ie_cleanup() from assigning a wrong
  // button. When a button is pressed (thus $_POST['op'] is set), then this
  // button will still set $form_state['submitted'],
  // $form_state['submit_handlers'] and $form_state['validate_handlers'].
  // This problem does not exist when AHAH is disabled, because then the
  // assumption is true, and then you generally provide a button as an
  // alternative to the AHAH behavior.
  $form_state['submitted'] = TRUE;

So you should be able to get it to work by setting this variable in your AHAH callback function.

beowulf1416’s picture

additionally, I add a handler to the jquery ajaxStop event so I can clear the errors that are set on the form.

$().bind("ajaxStop",function(){
  $(".error").removeClass("error");
});
cibonato’s picture

If the Save button is simply moved before the "Submit the type" button in the code, the form will be passed through its submit handler instead and the result is rather confusing...

Adding #ahah-selects like this via hook_form_alter will not work as intended because the original form's Submit button will be encountered before the button added by the form altering implementation. This is most likely to cause the form values to be saved to the database "behind the user's back" (if they happen to validate).

It's exactly the very same problem I'm dealing with right now. I have a custom content type created through Drupal web interface and it has some CCK fields. I can alter it using hook_alter_form() (I created a custom module to do it). So, I'm using the hidden button approach to solve the problem AHAH + select list, but the only submit handler executed is that bound to Save button.

Maybe it could sound too naive, but is not it possible to put this hidden button before the Save button through hook_alter_form()?

Greetings..

cibonato’s picture

Yes. I've written the modules a few months ago. In the mean time, I got a chance to delve quite deep into this. The messy execution of submit handlers really is a problem.

A good help is Wim Leers' AHAH helper module. About the problem you describe, I find this in the comments:

// If the form is being rebuilt due to something else than a pressed button,
// e.g. a select that was changed, then $_POST['op'] will be empty. As a
// result, Forms API won't be able to detect any pressed buttons. Eventually
// it will call _form_builder_ie_cleanup(), which will automatically, yet
// inappropriately assign the first in the form as the clicked button. The
// reasoning is that since the form has been submitted, a button surely must
// have been clicked. This is of course an invalid reasoning in the context
// of AHAH forms.
// To work around this, we *always* set $form_state['submitted'] to true,
// this will prevent _form_builder_ie_cleanup() from assigning a wrong
// button. When a button is pressed (thus $_POST['op'] is set), then this
// button will still set $form_state['submitted'],
// $form_state['submit_handlers'] and $form_state['validate_handlers'].
// This problem does not exist when AHAH is disabled, because then the
// assumption is true, and then you generally provide a button as an
// alternative to the AHAH behavior.
$form_state['submitted'] = TRUE;

So you should be able to get it to work by setting this variable in your AHAH callback function.

If I set $form_state['submitted'] = TRUE then nothing happens and some debug could confirm that no submit handler is executed at all when the select list change.

I'd like to post this comment as a matter of feedback.

Greetings.

joachim’s picture

These example modules should perhaps be submitted / merged into http://drupal.org/project/examples

greg.harvey’s picture

There are already AHAH examples for Drupal 6 in the Examples module, according to the project page. I have never reviewed/compared them to the discussion here.

joachim’s picture

Would probably be best to merge into the examples module, and then put a link in documentation to the project page. That way we avoid duplication; and development of the examples has its own issue queue, repository, etc.

arianek’s picture

Project: Documentation » AHAH helper
Version: » 6.x-6.x-dev
Component: Correction/Clarification » Documentation
Issue tags: +Documentation, +ahah, +examples

moving this to AHAH queue... not sure whether this needs vetting...

webchick’s picture

Project: AHAH helper » Documentation
Version: 6.x-6.x-dev »
Component: Documentation » Correction/Clarification

AHAH Helper is actually just a contrib module. These comments seem to be about a page referencing Drupal core's handbook docs about core's AHAH library, not AHAH Helper module, so restoring back to the Documentation queue. Sorry if this is incorrect.

ookami.kb’s picture

I've tried to write a code with this example, but i've encountered one problem. When I submit form without AHAH it works normal, but if i submit it with AHAH, the data in first select ('kind' property is not saved even in $_POST - drupal does not send it)

PrplPplEtr’s picture

Category: task » bug

...not sure if something has changed in core since this was posted, but these examples seem to be broken. The modules install fine, and I can view the form examples, but, regardless of whether I use "button" or "select", the second select not only doesn't update when the first is changed, it simply disappears.

jhodgdon’s picture

Title: AHAH done correctly » AHAH done correctly [suggested examples maybe?]
Project: Documentation » Examples for Developers
Version: » 6.x-1.x-dev
Component: Correction/Clarification » AJAX Example

Actually, arianek's suggestion was to get this into the Examples project, so let's try that.

rfay’s picture

So I haven't looked at these (didn't know about this thread) but does the D6 ahah_example not cover this material?

I should note that if this uses ahah_helper, it wouldn't be a candidate for Examples because we only do core stuff. (Just have to draw a line somewhere....)

Mile23’s picture

Status: Active » Postponed (maintainer needs more info)
Mile23’s picture

Issue summary: View changes
Status: Postponed (maintainer needs more info) » Closed (won't fix)

It's end-of-life time for the 6.x-1.x branch of Examples. See #2642596: D6, D7 Roadmap for Examples