Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
I have a form when first renders it outputs a form 'item'. Clicking a button to edit the 'item', ctools ajax is used to swap in a form 'textfield'. The 'textfield' has a #default_value set but it does not get applied. I changed the #type to 'textarea' and the #default_value gets applied correctly for that just not for 'textfield' #type. It appears to be looking for some data from the POSTed values. If I set a 'hidden' #type on the initial page, then the AJAX added 'textfield' does use the #default_value. Any ideas?
Comments
Comment #1
merlinofchaos CreditAttribution: merlinofchaos commentedSince you've added it later, when fapi is processing it, it probably thinks that there was a value in the $_POST even though there wasn't one (which I believe can happen if the textfield is blank; it won't actually post a value). FAPI can't tell that the textfield wasn't actually *there* before, it just says "Hey, a post, a textfield, they must've made it blank."
I don't think there's much CTools can really do about that, if my assumption is correct. (To be sure you'd probably have to debug the fapi submit process to see if that's the case).
Comment #2
greenSkin CreditAttribution: greenSkin commentedShouldn't the FAPI tell if $_POST['my_textfield'] isn't set that the textfield didn't exists before? I only ask because I'm not very familiar with the backend processes of the FAPI. Would this be more of a Drupal issue?
How come if the #type is 'textarea' the #default_value does get set?
Comment #3
merlinofchaos CreditAttribution: merlinofchaos commentedUh. I thought I explained that.
It looks in $_POST and says "Hey the user sent a value: Blank" and it sets it to blank.
FAPI does not know that the user did not send a value, because it does not understand the idea that fields were added since the user submitted. That isn't normally possible in the world of FAPI.
Comment #4
greenSkin CreditAttribution: greenSkin commentedSorry to make you repeat yourself, I'm just not understanding or I am not explaining myself fully. If on the AJAX call (with $_POST values) I am trying to add a $form['my_textfield'] that was not part of the form originally. If I am now understanding you, the FAPI when building the form with 'my_textfield' simply notes that there is a $_POST and thus simply does $form['my_textfield']['#value'] = $_POST['my_textfield']? Shouldn't it go farther and say if isset($_POST['my_textfield']) then set the value accordingly?
Comment #5
merlinofchaos CreditAttribution: merlinofchaos commentedNo, because if a textfield is there, and the user enters no value, it will not appear in the post.
The post will look exactly the same if 1) the textfield was not actually in the form or 2) the user had no value in the textfield.
Comment #6
greenSkin CreditAttribution: greenSkin commentedOkay, then how does the 'textarea' #default_value work?
Thanks for your help and understanding.
Comment #7
merlinofchaos CreditAttribution: merlinofchaos commentedIt works the same way as any other default value. Normally, the first time a form item is rendered, there was no post at all, so FAPI does not try to look at what the user submitted. Thus, default values get used.
Comment #8
greenSkin CreditAttribution: greenSkin commentedHuh? I'm not sure you're understanding my question. Let me start from the beginning with some example code.
Initial form. Assume a submit button that uses ctools ajax is included.
The form ctools ajax returns. The #default_value here does not get utilized.
If we simply change the #type in the form above to 'textarea' instead of 'textfield', the #default_value does get set properly. The only thing different is that it is a textarea instead of a textfield.
So why does the #default_value work for the 'textarea' but not the 'textfield'.
Does my question make sense now? Am I explaining myself better?
Comment #9
merlinofchaos CreditAttribution: merlinofchaos commentedI don't know why we're having this conversation here, at this point. This is all FAPI stuff. AJAX or otherwise, CTools doesn't have anything to do with it.
And I'll point out that Page Manager and Panels have a lot of AJAX forms that use textfields that work just fine in normal situations, so I don't think this is a universal thing you're talking about. Someone would've noticed.
Comment #10
greenSkin CreditAttribution: greenSkin commentedOk, that is what I was wondering. I only figured to look for help starting with ctools support. Thanks for your help and patience. Simply the textfield's #default_value should in fact work, thus it must be a problem with my code.
Comment #11
greenSkin CreditAttribution: greenSkin commentedI've been digging around more in the Drupal form code as well as ctools form code and I believe I found the issue that I am having. To start with I want to state that I've tested with a basic Drupal form that rebuilds itself upon submit and a ctools ajax form that rebuilds itself on ajax submit, then I monitored the differences.
With the basic Drupal form the added textfield's #default_value corrects gets set unlike with the ctools added textfield.
I noticed in ctools_rebuild_form() that the $form['#post'] is populated with $form_state['input'] whereas the drupal_rebuild_form() sets both $_POST and $form['#post'] to array(). Upon my testing, changing the $form['#post'] to an empty array successfully set my textfield's #default_value. I am sure there is a reason that ctools populates the $form['#post'] in contrast to what drupal_rebuild_form() does, and I would like to understand why.
Secondly, on a Drupal code side of things with the assumption ctools_rebuild_form() does in fact populate the $form['#post'], the function form_type_textfield_value() checks if $edit !== to FALSE. When the $form['#post'] is populated for a form element the $edit that gets passed to form_type_textfield_value() is set to NULL and not FALSE. The $edit is set to NULL when the form elements parent key does not exist inside of the #post.
So now my questions are first why does ctools populate $form['#post'] upon rebuild? Secondly, does a bug actually exist and is it in ctools or Drupal?
I will open an issue for Drupal concerning the function _form_builder_handle_input_element() dealing with this $edit variable. There are a couple of IF statements that check 'isset($edit)' when $edit gets set just a few lines above it to $edit = $form['#post'].
Comment #12
merlinofchaos CreditAttribution: merlinofchaos commentedCTools populates $form['#post'] on rebuild because if you don't, then you lose user set values on rebuild. This is something I've never understood in Drupal's form system. I guess the people using rebuild in core Drupal don't seem to care about that, but losing user set values is a major WTF in my experience.
Comment #13
greenSkin CreditAttribution: greenSkin commentedThat makes sense, though don't the user set values get passed via $form_state['values']?
Comment #14
merlinofchaos CreditAttribution: merlinofchaos commentedNope. That's a write bucket. Data isn't read in from it. :/
Comment #15
greenSkin CreditAttribution: greenSkin commentedOkay, I'll build my forms in a different way than simply rebuilding them.
Whats your thought on the form_type_textfield_value() issue I had mentioned in #11? Does it warrant switching this issue to Drupal?
Comment #16
merlinofchaos CreditAttribution: merlinofchaos commentedI admit I'm not sure. I would probably create a new issue and reference this one, but with the comments that are already in here I"m pretty sure moving this to Drupal would get a lot of nothing because few people would read all the way through and understand what's going on. =)
Comment #17
dkinzer CreditAttribution: dkinzer commentedI am having this same issue. The '#default value' for field type 'textfield' are not being set when a form is rendered in the ctools modal window. If I set any other fieldtype '#dafault value' it gets set without any issues. When I run the same form without AJAX (no ctools modal window) the default value for the textifield type is set without issues.
When I inspect the JSON string that is returned, I notice that it does not contain the '#default value' I set in my form definition array. So I suspect that it must get lost on the server side code (perhaps when the JSON string is created).
When I do print_r of the $form array at end of function ctools_build_form, I can see that the '#default value' is set:
Array ( [indicator] => Array ( [#type] => fieldset [#title] => Office Location [a_multi_zip] => Array ( [#type] => textfield [#default_value] => 12345 ...
The following is the json response for the form above, you will see that the '#default value' does not appear in there even though it was set in the form definition array as shown above:
[ { "command": "modal_display", "title": "Become a member", "output": "\x3cdiv class=\"messages\"\x3e\x3cdiv class=\"messages status\"\x3e\nThe zip value is \"\x3cem\x3e\x3c/em\x3e\"\x3c/div\x3e\n\x3c/div\x3e\x3cform action=\"/test/?q=membership_wizard/ajax/go\" accept-charset=\"UTF-8\" method=\"post\" id=\"membership-wizard-form\"\x3e\n\x3cdiv\x3e\x3cfieldset\x3e\x3clegend\x3eOffice Location\x3c/legend\x3e\x3cdiv class=\"form-item\" id=\"edit-a-multi-zip-wrapper\"\x3e\n \x3clabel for=\"edit-a-multi-zip\"\x3eInsert the zicode for where your office is: \x3cspan class=\"form-required\" title=\"This field is required.\"\x3e*\x3c/span\x3e\x3c/label\x3e\n \x3cinput type=\"text\" maxlength=\"5\" name=\"a_multi_zip\" id=\"edit-a-multi-zip\" size=\"10\" value=\"\" class=\"form-text required\" /\x3e\n\x3c/div\x3e\n\x3c/fieldset\x3e\n\x3cinput type=\"submit\" name=\"op\" id=\"edit-previous\" value=\"Previous\" class=\"form-submit\" /\x3e\n\x3cinput type=\"submit\" name=\"op\" id=\"edit-next\" value=\"Next\" class=\"form-submit\" /\x3e\n\x3cinput type=\"hidden\" name=\"form_build_id\" id=\"form-480da54e2001f338fee31070fb6ae68b\" value=\"form-480da54e2001f338fee31070fb6ae68b\" /\x3e\n\x3cinput type=\"hidden\" name=\"form_token\" id=\"edit-membership-wizard-form-form-token\" value=\"0349bacae8811cc1e99dbf1751f470fd\" /\x3e\n\x3cinput type=\"hidden\" name=\"form_id\" id=\"edit-membership-wizard-form\" value=\"membership_wizard_form\" /\x3e\n\n\x3c/div\x3e\x3c/form\x3e\n" } ]
Comment #18
dkinzer CreditAttribution: dkinzer commentedAdding $form['#post'] = array() directly to my form array definition also worked for me. And as far as I can see, I am not losing any set values moving back and forth between forms. Thanks so much for this work around! - Now I just have to make sure I only set it when the forms are being called via AJAX.
Comment #19
msonnabaum CreditAttribution: msonnabaum commentedI believe this actually is a core issue.
It comes down to form_type_textfield_value() only checking if $edit is FALSE, when in this case, its NULL.
The D7 version of form_type_textfield_value does check for a NULL value:
http://api.drupal.org/api/function/form_type_textfield_value/7
I've created a core issue for 6 with a patch to add this:
http://drupal.org/node/820858
That patch should fix the issues discussed in this thread.
Comment #20
merlinofchaos CreditAttribution: merlinofchaos commentedMarking this issue postponed based on #820858: Default value for textfield not set when $form['#post'] isn't unset
Comment #21
japerryClosing this issue as outdated as Drupal 6 ctools is not supported. If this issue is relevant for Drupal 7, feel free to re-open and mark for Drupal 7 (or 8)