See http://drupal.org/node/1059694
Steps needed:
1) Create the simplest multi-step form ever.
function a_form($form_state) {
$num = (int)$form_state['storage']['number'];
$form['Next'] = array(
'#type' => 'submit',
'#value' => $num.'++',
);
return $form;
}
function a_form_submit($form, &$form_state) {
$form_state['storage']['number']++;
}
2) Use the form, hit F5 at any time
3) Form will misbehave from the second submission onwards.
Expected behavior:
F5 (refresh) does not fire the submission handler, does not change the content of the form
Current behavior:
When refreshing on further pages of the multi-step form (after the 2nd submission onwards) the submit-handler is somehow fired changing the content of $form_state. In this example it takes you back to after the 1st submission. In more complex examples you get to a completely different place with no apparent logic. The submit handler is fired with submit buttons never pressed before etc.
Comments
Comment #1
operations commentedSame here ! that is incredible .. I can't find a solution for this. Please help !
Comment #2
davidsonjames commentedThis is the soultion I used. At the top of my form_submit I added:
And at the bottom of my form_submit I added:
This seemed to retain the form_build_id even when I refreshed my form mid-way through the stages.
I wrote a post on it here: http://davidsonj.com/blog/how-create-multi-step-form-drupal-7
Comment #3
operations commentedGood stuff! I'll try out your code next time I need a multi-step form. For the moment I think its neat.. Thanks
Comment #4
itsmyname commentedi have tested the solution from davidson james on drupal 7 - and it works perfect!
thanks james - you saved my day!
Comment #5
Johnny vd Laar commentedThis problem still persists in Drupal 7. The workaround from #2 still works in D7. I didn't check this for Drupal 8.
Comment #6
Johnny vd Laar commentedWhen I look in the function ajax_get_form then I see this:
So when I add this to the top of my _form function then I stay on the same page when I refresh the page. Only issue is that it tries to validate the current step (instead of the previous step which was the one being submitted and thus refreshed).
Comment #7
David_Rothstein commentedThere is no patch here, so nothing to commit...
Also, I think we should assume this affects Drupal 8 too, unless proven otherwise.
Comment #8
david_garcia commentedJust make sure that before rebuilding, you tell the form API to persist build_id during rebuilds...
Some reverse engineering to get that though, needs some documentation.
$form_state['rebuild_info']['copy']['#build_id'] = TRUE;
$form_state['rebuild'] = TRUE;
Comment #9
david_garcia commentedSorry, that same was said in #6, I just wasted my time.
Comment #10
joachim commentedWith a multi-step form that doesn't use AJAX, making that change doesn't work: reload the form *advances* it by one step.
The form I'm using is the Migrate Drupal wizard from https://www.drupal.org/project/migrate_d2d
Comment #11
joachim commentedI'm seeing this with the the following test code too:
Comment #12
david_garcia commented@joachim: The problem is that you are storing step data server-side. There might be another approach in wich you detect the real step by the means of the element triggering the submit.
BTW, in D7 there is no need to use $form_state['storage'], you can use any key in $form_state.
Try storing step information in a hidden field:
Comment #13
joachim commented> The problem is that you are storing step data server-side
Yes, since posting my comment, I've concluded the same thing.
Basically, there are two possibilities:
1. drupal_rebuild_form() keeps the old build ID. The re-sending of POST data by the browser means that the submit handler causes the form to go from the current step in the cached version to one step further: the user sees the form advance to current step + 1.
2. drupal_rebuild_form() makes a new form ID. The re-sending of POST data by the browser means that the submit handler causes the form to go from the first step to the second step: the user sees the form go back to step 2.
I don't see that there's a way for FormAPI to fix this: as you say, the client-side form needs to know what step it is currently on.
Comment #14
david_garcia commentedOne last finding: if you have a step in you multistep that does not have a submit button (for example a last step with a message) when the user refreshes (F5) on that last step form_state is lost and form completely rebuilt, because in the last build the server does not acknowledge the existance of a submit button and will not process the submit handler.
To overcome this, just make sure than on every form build you have at least 1 submit element.
I added a hidden one to every form build:
Comment #15
joachim commentedI've had a look at the form wizard in Examples module, to see how that works.
It *doesn't* use a hidden value -- but behaves correctly when any step is reloaded! What does it do differently?
Comment #16
joachim commentedThe crucial code is this in the submit handler for the next step:
I've filed #2370763: clarify how the form wizard deals with page reload on the Examples project.
Changing this to a support request, as Examples demonstrates that there's no bug in core.
Comment #23
Coyote6GraphX commentedI know this is old, but for others with the same issue on Drupal 7, I have a multi step form using ctools that I needed to save $form_state data to the session for, but needed a step to repeat as much as needed during the process. I was running into the same issue on page refreshes and couldn't get the other suggestions to work for my case, so this is my work around.
EDIT:
Removed old solution that turned out to only be sporadically working.
SOLUTION:
Used drupal_goto ('page/my/form/is/on') to redirect at the end of the _submit function to reload the page without the $_POST data and therefore, preventing _submit from firing upon page reload.
Comment #28
cilefen commentedI am closing this support request because there have been no recent comments.
The Drupal Core issue queue is not the ideal place for support requests. Consider other sources of support.