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

merlinofchaos’s picture

Status: Active » Closed (won't fix)

Since 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).

greenSkin’s picture

Shouldn'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?

merlinofchaos’s picture

Uh. 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.

greenSkin’s picture

Priority: Normal » Minor

Sorry 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?

merlinofchaos’s picture

No, 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.

greenSkin’s picture

Okay, then how does the 'textarea' #default_value work?

Thanks for your help and understanding.

merlinofchaos’s picture

It 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.

greenSkin’s picture

Huh? 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.

<?php
$form = array();
$form['subject_item'] = array(
  '#type' => 'item',
  '#title' => t('Subject'),
  '#value' => $subject
);
return $form;
?>

The form ctools ajax returns. The #default_value here does not get utilized.

<?php
$form = array();
$form['subject'] = array(
  '#type' => 'textfield',
  '#title' => t('Subject'),
  '#default_value' => $subject
);
return $form;
?>

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.

<?php
$form = array();
$form['subject'] = array(
  '#type' => 'textarea',
  '#title' => t('Subject'),
  '#default_value' => $subject
);
return $form;
?>

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?

merlinofchaos’s picture

I 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.

greenSkin’s picture

Ok, 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.

greenSkin’s picture

Category: bug » support
Status: Closed (won't fix) » Active

I'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'].

merlinofchaos’s picture

CTools 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.

greenSkin’s picture

That makes sense, though don't the user set values get passed via $form_state['values']?

merlinofchaos’s picture

Nope. That's a write bucket. Data isn't read in from it. :/

greenSkin’s picture

Okay, 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?

merlinofchaos’s picture

I 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. =)

dkinzer’s picture

I 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" } ]

dkinzer’s picture

Adding $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.

msonnabaum’s picture

I 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.

merlinofchaos’s picture

Status: Active » Postponed
japerry’s picture

Issue summary: View changes
Status: Postponed » Closed (outdated)

Closing 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)