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.
Hi
I have a simple use case and I cannot figure it out how to achieve this with ctools.
- Base is a simple form with some textfields.
- The user enter some numbers and presses "Submit".
- The fields are multiplied and the result is displayed below the submit button.
How can I change the form, that the "Submit" is done by Ajax (form validation and submission) and that after sucessfull validation the result ist displayed by javascript in the DOM?
I want to run through the normal submit / validate handler.
function my_calculator_simple_form($form_state) {
//Invitation Form
$form = array();
$form['size'] = array(
'#type' => 'textfield',
'#title' => t('Size'),
'#default_value' => '',
);
$form['weight'] = array(
'#type' => 'textfield',
'#title' => t('Weight'),
'#default_value' => '',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Calculate'),
'#weight' => 11,
);
return $form;
}
function my_calculator_invite_form_validate($form, &$form_state) {
$size = explode(' ', $form_state['values']['size']);
if (!is_numeric($size)) {
form_set_error('size', t('You have to enter at numeric value'));
}
}
function my_calculator_invite_form_submit($form, &$form_state) {
$result = $size * $weight;
//Display the result below the submit button using AJAX
//But how?
}
Comment | File | Size | Author |
---|---|---|---|
#37 | 1025448-ctools-ajax-form-example-37.patch | 8.64 KB | tic2000 |
#26 | ajaxify_comments.tar_.gz | 6.43 KB | tic2000 |
#14 | validate-fail.png | 7.83 KB | merlinofchaos |
Comments
Comment #1
ayalon CreditAttribution: ayalon commentedDoes anyone have an idea how to do this? This would be very helpful.
Comment #2
ayalon CreditAttribution: ayalon commentedI still have no clue, how to do this. Does anyone have a hint for me?
Comment #3
merlinofchaos CreditAttribution: merlinofchaos commentedHave you looked at the ctools_ajax_sample module? It contains a piece of code that does something like this, though without the form part actually involved.
Comment #4
ayalon CreditAttribution: ayalon commentedI have studied the code. The problem is, that forms can only be handlet "modal" or in a wizard way. My goal was to handle a form without a dialog and display the result in the DOM of the current page.
There is no example with a form, that allows me to submit the form (submit handler) and to render some result on the same page. Imagine a Vote form, you press vore and the form is hidden and the result is displayed in place of the form.
Comment #5
merlinofchaos CreditAttribution: merlinofchaos commentedOk, I see what you mean.
You can operate more or less how the wizard works, but use ctools_build_form() instead of the modal form wrapper. You test $form_state['executed'] to see if the form was submitted, and behave appropriately based upon $form_state['clicked_button'].
Let me know how far that gets you.
Comment #6
merlinofchaos CreditAttribution: merlinofchaos commentedHm. I'm not sure the above is clear.
This is pseudocode:
It's slightly more complicated than that if you want to operate both with and without a javscript context, since you need to detect $js and answer appropriately.
Comment #7
ayalon CreditAttribution: ayalon commentedDear merlinofchaos
Thank you very much for your response. I was not aware of the $form_state['executed'] variable and I will try to extend my example in the way you described. I will post my finding, as soon I was able to get a working example.
Comment #8
merlinofchaos CreditAttribution: merlinofchaos commentedIt's something ctools_build_from() added somewhere along the way because it was getting increasingly difficult to tell if a form had been successfully submitted or not.
Comment #9
ayalon CreditAttribution: ayalon commentedHi merlinofchaos
I tried to implement a simple example, but unfortunatley I was not successful. It's a small BMI Calculator. You enter your weight/height, press submit and then the form should validate. If not, display some errors. If it validates, replace the form with the results of the calculation.
bmi_calculator.module
As you can see, I added your code in the submit handler. But this is probably not the right place.
2 problems occur:
- The form validation throws an error (don't know why)
- The submit-handler is recursively executed and throws server error
- $form_state ['executed'] is always empty
Maybe you could explain me, where to add the pseudocode you wrote above. The problem is, that I have to start with the drupal_get_form() in the menu item to get the form displayed. The next action should occur, when pressing the submit button. This is the reason, why I put your code in the submit handler but probably this is wrong.
Comment #10
merlinofchaos CreditAttribution: merlinofchaos commentedYou don't add your code to the submit you add it after the call to ctools_build_form().
In my world, the _submit handler deals ONLY with the input parsing of the form, usually to copy raw form values into something that can be used later.
This means you can't use 'drupal_get_form' for your page callback, either. You'll need to add another callback.
Let me fiddle with this code, I bet I can get a working example fairly quickly.
Comment #11
merlinofchaos CreditAttribution: merlinofchaos commentedHere is a functioning example.
I would recommend doing the actual calculation in the submit. Something like:
And probalby using number_format and then outputing it via t().
Comment #12
merlinofchaos CreditAttribution: merlinofchaos commentedBTW, assuming a correct formula, can I turn this into an example for the examples module?
Comment #13
tic2000 CreditAttribution: tic2000 commentedStarting from the pseudo code in #6 I achieved the same result in an uglier way.
But what issue I have with both mine and that in #11 is that if the form doesn't validate it shows the red border around the form. If you submit correct values I found no way to get rid of that. If I try to the same command as when getting a validation error the form just disappears. If I try to do a ctools_build_form again when I submit the form with correct values the throbber will go on and on and I will get an error with an empty response.
Any solution for this?
If there is no easy solution I could build an ajax command to remove the error class from the inputs and add that in the submit (after
$commands[] = ctools_ajax_command_html('#bmi-calculator-result', $content);
)PS. Using this bmi example or just a simple multiplying form, yes, I think it would be great to add it to the example module.
Comment #14
merlinofchaos CreditAttribution: merlinofchaos commentedI don't understand. This is what I get when the form fails to validate:
Comment #15
tic2000 CreditAttribution: tic2000 commentedYes, my comment wasn't very clear.
From the image above. If you enter correct values and submit again the first field will still display the red border even if the value is valid and you get your result displayed.
First step
Second step
Comment #16
merlinofchaos CreditAttribution: merlinofchaos commentedAH!
Ok, that's because it only sends the result back, it doesn't rerender the entire form.
Add 'rerender' => TRUE to the $form_state (right under the no_redirect => TRUE) and send the form back:
Do that *before* the replace on the calculation since we put that in the form. Meaning the calculation result area should probably be outside the form.
Comment #17
tic2000 CreditAttribution: tic2000 commentedNow it works like a charm.
Comment #18
merlinofchaos CreditAttribution: merlinofchaos commentedChanging to a task to integrate this into the CTools ajax sample.
It definitely needs a bunch of cleanup. Also the right formula would be handy so it actually calculates.
AS an example it would be great to show both the modal and non-modal versions, as that would allow a nice side by side comparison.
Comment #19
tic2000 CreditAttribution: tic2000 commentedYou can see the formula at http://www.whathealth.com/bmi/formula.html if you want to use the BMI example.
Comment #20
ayalon CreditAttribution: ayalon commentedHi merlinofchaos, hi tic2000!
Thank you very much for your help. I think drupal is the only CMS, where a "god-like" developer helps a beginner in such a patient way. I will try to convert this example including the correct form in to the AJAX sample module. It seems, that more than only two people are interested in handling forms in such a brilliant way *g*.
Give me some time to do dis task.
Comment #21
tic2000 CreditAttribution: tic2000 commentedOne more thing. In this example the id of the form to replace it's hardcoded. It works if this is the only form of this type on the page. But if we do a vote form for example and display it on node teasers then the id will always point to the first form on the page.
My fix was to add
'want form' => TRUE
to the form state array.Then I replace
with
and
with
Now my question is, given the fact that ctools is used a lot with ajax, wouldn't be useful to add a line in ctools_build_form() where we do
$form_state['dom id'] = $form['#id']
so we have that id always available without extra lines of code?If you consider it useful and if I can get my head around the new git stuff needed to write a patch, I will open a new issue and provide a patch for this.
Comment #22
merlinofchaos CreditAttribution: merlinofchaos commentedIf I'm ever in a situation where I don't KNOW the #id of the form, I will usually wrap the form in another id that I can be sure of, or add classes to the form via $form['#attributes'] in order to perform operations on it.
You can also store whatever other data you need in $form_state, so if there's some variable in the form #id that you need, you can do that as well.
I find it's really rare to have multiple forms of the same type on a page (Drupal has troubles with this in general) so I think the 'want form' parameter is a perfectly good way of handling it.
Comment #23
drurian CreditAttribution: drurian commentedThanks for the provided example. Just wanted to notice though that it doesn't work with the javascript turned off. How would you fix it?
Comment #24
tic2000 CreditAttribution: tic2000 commentedModify the line
to
Then of course add the same condition with
!$js instead of $js
were you code your output for the non js page.That condition will make sure that you won't receive a json string as output if js is not enabled.
PS: For what I did using this code as a starting point and it also degrades see http://d6.miidecuvinte.ro/node/1
Comment #25
drurian CreditAttribution: drurian commentedThanks, do you mind posting your code somewhere?
Comment #26
tic2000 CreditAttribution: tic2000 commentedHere it is.
Note that it also requires comment_bonus_api module for it to work. And of course ctools.
Comment #27
drurian CreditAttribution: drurian commentedFor whatever reason, using
didn't work for no js (missing from the $form_state I guess). However, I was able to simply append some stuff to $output and it got rendered on submit.
Comment #28
drurian CreditAttribution: drurian commentedOk, got another question. I tried to combine this example with the one from here. It goes like this: a user chooses from one of the radio buttons, on submit the map of US+Canada is going to load with links to another page with the maps. Depending on their choice, the map of the selected country is loaded. The last step is where I get ajax loading errors. No js works fine.
My code looks like this:
countries-map.tpl.php
regions-map.tpl.php
Comment #29
tic2000 CreditAttribution: tic2000 commentedI have one more question regarding this.
If I use $form_state['rerender'] = TRUE the form will be retrieved from the cache and will have the last values I entered.
What can I do to create a clean form after the submit, like the first time I opened the page? Looking in form.inc I saw that $form_state['input'] is used to store some info about the form. I tried to create a new form inside
if (!empty($form_state['executed']) { ... }
but this also stops the form from submitting correctly. I tried to this with a node form and doing that the node uid is lost.Comment #30
merlinofchaos CreditAttribution: merlinofchaos commentedFor that you actually need to rebuild the form. I recommend using ctools_build_form() a second time to get a completely clean form, and make sure $form_state['input'] = array() so it doesn't pull values from $_POST.
Comment #31
tic2000 CreditAttribution: tic2000 commentedOK, disregard that.
It worked with 'input' = array(). My error was that I was loading the node to display it and then forgot to create a new "empty" one for the new form.
Comment #32
ayalon CreditAttribution: ayalon commentedCould you please post your code so we can include it in the sample? Thanks
Comment #33
tic2000 CreditAttribution: tic2000 commented@merlinofchaos
That was exactly what I did. But like I said, I wanted to render the node and when rebuilding the form I used the same variable, which of course had all the node data. So I created a new empty variable for a new node.
The function used is actually this
The node type is "status".
ctools_add_js('livingclassic_profile', 'livingclassic_profile');
is a js file where I added some custom ctools ajax commands (like the one I use laterctools_ajax_command_remove_messages(6000);
to remove the status messages after 6 seconds.The only problem I had left is that if the node has attachments (like an image field), after the first save which works fine, on the second, uploading a file will throw a validation error saying the form is not in cache. If I try to save the second node without a file, everything works.
I remember I read an issue about this, but there was no solution if I remember correctly.
PS. Next week I get back at home and if no one provides a patch I will try to make one (a good way to learn how to use git now).
Comment #34
drurian CreditAttribution: drurian commentedFollow-up: correcting the typo took care of my problem. However, I needed to bind Ajax behavior to area tags for my image maps, so I added this javascript to my module:
Is this the proper way to do it?
Comment #35
mpaler CreditAttribution: mpaler commentedHi,
I'm running the example posted in #7 above and it works nicely. Thankyou!
However, I'm definitely observing the behavior described in #830382: Unwanted / duplicated temporary css files if CSS Optimization is enabled.. Many stylesheets are being injected into the bottom of my page like this:
Can we rule out this example being the culprit? Should I be looking elsewhere?
I'm using Zen theme. Thanks for any insights.
Comment #36
mpaler CreditAttribution: mpaler commentedBack to the original topic at hand: ctools ajax documentation...I thought I would pass on a tip that may or not be obvious, nevertheless, it was a very pleasant discovery to me.
All the examples in the ctools_ajax_sample module and the examples above use menu callbacks that render a complete page. I was looking for a solution that would put an ajaxified form directly within all nodes of a certain type. It turns out you can render the menu callbacks into a node template variable and have them render AND work as expected.
For example, using the example above of the BMI calculator, you can put the following in your template.php file:
then, anywhere in your node template you can render the form fully ajaxified using
Comment #37
tic2000 CreditAttribution: tic2000 commentedOK, here's a patch. I hope it's the correct format.
I made sure it works with and without javascript enabled. The only difference between the 2 is that the validation error are displayed above the form in the javascript form.
Now, I also added a "chart" for the results. If it adds too much complexity it can be removed, but I think it's also a nice example of how to use ctools to alter a page through ajax.
Comment #38
mpaler CreditAttribution: mpaler commentedJust tried this patched demo. When I run it with core css aggregation turned on, all the css files are loaded at the bottom of the page thus overriding my theme in horrible ways. The js files are reloaded into the . Could this be related to the code in the demo?
[edit]
I fixed this by adding
to the top of ctools_ajax_sample_bmi_page()
as follows:
Comment #39
zamir CreditAttribution: zamir commentedIf someone have an example for drupal 7?I think the above code can not be executed correctly in d7,at least the function 'ctools_build_form' do not exsit.
Comment #40
Raf CreditAttribution: Raf commentedSeems like Drupal core took some of Ctools' functionality in D7. I just found this link, with all upgrade changes you have to make in your code in order to make it work with D7.
Specific to ctools_build_form, it says the following:
Comment #41
shantanu1 CreditAttribution: shantanu1 commentedWrite your first ajax form submit.....
Thanks
Shantanu Karmakar
email:shantanu2683@gmail.com
Comment #42
andypostThis needs to join forces with #1662570: CTools exportable example and find proper home for a new module
Comment #43
dhineshkumar CreditAttribution: dhineshkumar commentedNice stuff
It working. Thank you.