Problem/Motivation
When a node form fails validation (i.e. a required field is not filled) and displays the node form again.. the hook_form_alter is not being executed, so any alterations done by any module on a node form are not executed.
This is no longer true in Drupal 8. So the issue affects Drupal 7 and 6 (though D6 is no longer supported.)
Proposed resolution
TBD
Remaining tasks
Provide steps to reproduce
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#13 | drupal-6.19-Form_Validation_Form_Alter.patch | 796 bytes | sgtpep |
Comments
Comment #1
grendzy CreditAttribution: grendzy commentedI couldn't reproduce this issues with Drupal 6 HEAD. I tested this using path module (the "URL path settings" fieldset is added via hook_form_alter).
Is it by chance a multi-part form? There is some weirdness going on there, see #302240: button broken due to fix various problems when using form storage
Comment #2
joelstein CreditAttribution: joelstein commentedI'm having the same problem. My "custom" module's hook_form_alter is simply not getting executed when re-presenting the form after failed form validation.
Comment #3
Dave ReidI can't duplicate this either with base functionality. Make sure nothing is altering the form to be cached as that might cause this to be skipped after validation.
Comment #4
slashwalk CreditAttribution: slashwalk commentedYes i can confirm this happens to me with a custom module, i make field alterations on the module. I went with another approach i decided to create a new cck widget for the feature i wanted.
Comment #5
ExTexan CreditAttribution: ExTexan commentedPlus 1 on this... I'm doing hook_form_alter on the user_register form. In it, I drupal_set_title('Register) - but noticed that it reverts back to "User Account" if errors are encountered during validation. I added some dpm(xxxx) diagnostic displays, but they don't show after validation either.
Comment #6
bmdhussain CreditAttribution: bmdhussain commentedI got the same issue in ipaper node form. I have added one validation method in the form to validate file attachments. It's not triggering the validation. Any clues?..
Comment #7
mrfelton CreditAttribution: mrfelton commentedSame problem for me. Using hook_form_alter on a node add form. When validation of the form fails, the form_alter is not called when the form is rebuilt.
Comment #8
mrfelton CreditAttribution: mrfelton commentedActually, just noticed that
mymodule_form_alter()
does get called after failed validation. But the more specific,mymodule_form_contet_type_node_form_alter()
does not.EDIT: Actually, that was only because I had the drupal for firebug module installed. After uninstalling that module, form_alter is not called after a validation failure at all.
Comment #9
mrfelton CreditAttribution: mrfelton commentedActually, I take it all back, form_ater is apparently not supposed to get called twice as it is cached. I got around this by using #after_build to add js to my page (which is what I was trying to do in form_alter) - see http://www.appnovation.com/drupaladdjs-and-drupaladdcss-hookformalter-an...
Comment #10
xandeadx CreditAttribution: xandeadx commentedsubscribed
Comment #11
Jaypan CreditAttribution: Jaypan commentedUsing #after_build worked for me too. I did it like this:
This way the submit handler is always attached to the form.
Comment #12
sgtpep CreditAttribution: sgtpep commentedsubscribed
Comment #13
sgtpep CreditAttribution: sgtpep commentedHere the patch that works for me.
Comment #14
grendzy CreditAttribution: grendzy commentedSee #9 regarding #after_build.
Comment #15
sgtpep CreditAttribution: sgtpep commentedFew Drupal modules uses hook_form_alter for form modification and requires calling of this hook even if validation fails. Some of known to me modules with this issue: markitup, twitter-3.x. So this change of Drupal core is not backward compatible.
Comment #16
grendzy CreditAttribution: grendzy commentedIf this is an issue of backwards compatibility, can you help locate the commit that introduced the change?
Comment #17
sgtpep CreditAttribution: sgtpep commentedNo, I was wrong, I couldn't find this change. But this behavior of hook_form_alter is strange and unexpected in my opinion.
Comment #18
grzegorz.bartman CreditAttribution: grzegorz.bartman commentedsubscribing
Comment #19
kenorb CreditAttribution: kenorb commentedAre you using some kind of $form_state['redirect']?
Maybe that's the reason? It's redirected before the hook_form_alter is called.
Comment #20
aacraig CreditAttribution: aacraig commentedI can confirm the same behavior, and I'm not doing any redirection.
Comment #22
Alan D. CreditAttribution: Alan D. commentedThis should be either a "won't fix" or an "active" bug, depending on what the powers to be decide on. AKA is is a fairly big developer wtf in the rare use case where is shows itself.
Considering that it is an api change, I've tagged as "won't fix".
Good news is that it appears fixed in Drupal 7.
Comment #23
arosboro CreditAttribution: arosboro commentedDoesn't look fixed in D7 to me, I had to resort to using #after_build which gets called every time the form loads.
Comment #24
Alan D. CreditAttribution: Alan D. commented@arosboro
I ran some tests at the time of comment #22 and these failed to get called on Drupal 6, but were called on Drupal 7. So, was the alter() in the main *.module file or via an include somewhere? If in an include, try including the alter hook within the main module file and if that fails, I think that this may need to be reopened!
Comment #25
arosboro CreditAttribution: arosboro commentedThe alter is in my main module. I'm using hook_form_FORM_ID_alter(), but I changed it to hook_form_alter() with a case for my form_id, and the dpm statement I added still did not appear on the validation error page.
Comment #26
arosboro CreditAttribution: arosboro commented@Alan D
Actually, I just realized this was on Drupal 7.0. I've upgraded to 7.12 and the hook is evaluated as expected. This issue can remain closed.
Comment #27
arosboro CreditAttribution: arosboro commented@Alan D
I need to take back my response in #26. It seems as if hook_form_alter was called on the validation error page after I first installed 7.12, but after using the form for a while it reverted back to using a cached form without calling hook_form_alter.
Comment #28
Alan D. CreditAttribution: Alan D. commentedSounds more like a panel caching issue from that description. Can you tell us the steps to replicate or at least what form?
Comment #29
manuelBS CreditAttribution: manuelBS commentedI got the same problem (I use panels and display suite, too). I solved the problem by setting
With disabled form cache hook_form_alter is called even after form validation.
Comment #30
crevillo CreditAttribution: crevillo commentedthis indeed fix the the first part. hook_validate seems to be executed again, but i've lost my ajax functionality.
Comment #31
jide CreditAttribution: jide commentedYou could try using #after_build :
Comment #32
Alan D. CreditAttribution: Alan D. commentedOn a clean install, can someone give the minimal steps to replicate that do not involve adding panels, etc. A simple module may be needed to demonstrate the issue.
If you can, re-open. There is enough activity here to suggest that it is an active bug, but I have tried unsuccessfully to mimic on Drupal 7 (and I do not have Drupal 8 running).
Comment #33
kenorb CreditAttribution: kenorb commentedMaybe it'll be better to report it against Panels module.
It'd be good, if somebody can dump the whole $form and $form_state, which external callbacks are defined (in form_alter and in validation hook).
Comment #34
Damien Tournoud CreditAttribution: Damien Tournoud commentedThis is by design: hook_form_alter() is only called when the form is actually rebuilt. If you want to run something every time the form is rendered, add a #pre_render callback to the top-level $form.
Comment #35
Jaypan CreditAttribution: Jaypan commentedCan you add that in hook_form_alter()?
Comment #36
Alan D. CreditAttribution: Alan D. commentedyes. Another wtf though, this must be in the defined path for Drupal 7 at least.
Comment #37
zach.bimson CreditAttribution: zach.bimson commentedYou are going to have the same problem with this as you are with putting your alter code in hook_form_alter()...
If the prebuild is called within that hook its not going to get called when its validated.
Comment #38
zach.bimson CreditAttribution: zach.bimson commentedDoes anyone have any other suggestions?
Comment #39
Damien Tournoud CreditAttribution: Damien Tournoud commentedNot sure what you are talking about. If you want to do something everytime the form is rendered:
When the form is loaded from the cache, it would already be altered, so your #pre_render will already be there, and will be executed when the form is rendered.
Comment #40
zach.bimson CreditAttribution: zach.bimson commentedI'm talking about the fact that form_alter is only called once...
Even if i turn page caching on the node form im altering does not keep the changes made in in my custom function.
The pre_render is declared in form alter so will not be called/ defined again once the validation is called.
I know drupal core should cache it but it doesnt seem to be.
Im also now getting this error
Notice: Undefined index: #build_id in drupal_process_form() (line 941
Code sample:
Comment #41
zach.bimson CreditAttribution: zach.bimson commentedSorry to be persistent but does anyone have any suggestions as to how i can get this to work?
My form alter elements are required after validation.
Thanks in advanced.
Zach
Comment #42
Jaypan CreditAttribution: Jaypan commented#pre_render is not a valid option, as there is no access to the $form_state in #pre_render.
Comment #43
Jaypan CreditAttribution: Jaypan commentedComment #44
pingers CreditAttribution: pingers commentedThis cannot be reproduced in a clean D7 HEAD install.
Using a module with the following...
I see abc printed under the title field for page nodes every time.
E.g. Fill in 123 in the body field and hit save, title is required, validation fails, test string "abc" is present under Title field, indicating that hook_form_alter (in each incarnation) is taking effect. Using a debugger, I can confidently say that each and every one of those hooks fired, validation failure or not.
Soo... what's the deal? Sounds like custom code breakage to me. Really going to need more info about what is broken and how to break it, or this is closed - cannot reproduce.
Comment #45
Alan D. CreditAttribution: Alan D. commented@Jaypan You can use an afterbuild instead then. It only has context to the element and form state. i.e.
Comment #46
dahousecat CreditAttribution: dahousecat commentedThe solution in post #11 works for me - thanks for the code - was getting a bit worried for a second there!
EDIT: Using the after_build callback introduced some new errors for me (can't add new value for fields that allow multiple values) so I've used pre_render instead and now is all working great.
Comment #47
fire-wolf CreditAttribution: fire-wolf commentedCode sample from #40 works as expected while there is no drupal_add_js in code I move to _after_build. I have to add javascript and after validation errors there is something strange - submit button doesn't work. Tried to put drupal_add_js into hook_init() - the same. Tried to load javascript with .info directives - the same. I did a little changes to submit button - changed only title and position in form. Can anyone recommend something?
Comment #48
fire-wolf CreditAttribution: fire-wolf commentedOkay, #pre_render option works too. But it have some limitations. If I define button in _form_alter() using:
$form['buttons']['some-container']['next'] = array(... button parameters...) then I can access this button with javascript with commands like $("#edit-next").click(...), but if I define the same button in #pre_render specified function, then the same button doesn't have id="edit-next". It doesn't have any id. So the question is what to do? Do I need to rebuild form? #pre_render function doesn't receive $form_state and therefore I cannot specify $form_state['rebuild']=TRUE. I tried to rebuild in #after_build function, but then submit button looses its functionality - form after submission returns in state edit with no errors/messages. I'm stuck. If I don't use javascript, then all seems to be okay, but I need javascript there to move buttons at right place.
If it "works as designed" then I call for designer!
Comment #49
rooby CreditAttribution: rooby commentedOne thing to note about #after_build is that it gets run before you know whther or not validation fails, so if you need to distinguish between a form that has been submitted and has failed validation and a form that didn't fail validation you cannot do it there.
Comment #50
illeace CreditAttribution: illeace commentedRelated to this discussion, any ajax form elements (like the "Add more items" button) will automatically turn on form caching, which (as pointed out above) will result in form alter functions not being called during validation.
If you're just trying to add some javascript to the form (as I am), the "after_build" workaround mentioned in #31 is a good solution. Keep in mind that the "after_build" callback function needs to return the $form variable, otherwise you're going to end up with an error along the lines of:
Comment #51
ankur140290 CreditAttribution: ankur140290 commentedI don't know how many of you have tried this but the best and simple way to do this disable cache of the required form as mentioned by comment #29.
Comment #52
Alan D. CreditAttribution: Alan D. commented"disable cache" and you should see the AJAX calls fail :(
Comment #53
TechNikh CreditAttribution: TechNikh commented#11 worked for me
Comment #54
webcat7 CreditAttribution: webcat7 commented#11 worked for me too
Comment #55
StephenRobinson CreditAttribution: StephenRobinson commentedsame issue in D7, think it is just the javascript that is missing
Comment #56
Elin Yordanov CreditAttribution: Elin Yordanov commentedNone of the proposed solutions are satisfying. Setting no cache the ajax functions not working any more, and #after_build is limiting. I am not agree with that hook_form_alter should not be called "by design" after a failing validation.
Comment #57
rooby CreditAttribution: rooby commentedYeah, it would be good to be able to handle the case in #49.
Comment #58
acb CreditAttribution: acb commentedHas anyone come up with a solution for this or does it simply languish?
Comment #59
Alan D. CreditAttribution: Alan D. commentedAnyone tried to replicate this in Drupal 8.x? This is probably the only version that could potentially get an API change like this in sadly, if it is not too late already....
Comment #60
kristiaanvandeneyndeI just ran into this as well and I must say this is an extremely annoying 'design decision' to call 'working as intended'.
I could agree that you do not need to call hook_form_alter() again on cached forms and I could even agree that this causes any form-unrelated actions in hook_form_alter to be discarded (drupal_set_title(), drupal_add_css(), ...). After all, we have other means such as #attached for that.
What I simply find weird is that ANY form alterations from hook_form_alter are discarded altogether: extra validation handlers, altered form elements (labels, descriptions, ...). Those are clearly meant to be there every single time the form is loaded, why else would you have a function named hook_form_alter()?
Furthermore, I find the 'solution' to be very ugly: Register a hook_form_alter() to register a mymodule_after_build() for EVERY form you want to alter (I sense a "Yo Dawg" meme lurking around the corner). That's twice the amount of functions for half the amount of developer fun.
TL;DR: What's the point of having a function to alter forms with if it doesn't always work?
Comment #61
acb CreditAttribution: acb commentedYes… seems like a catch-22. #pre_render funcitons added in the form_alter also get lost.
Comment #62
arif.stalker.majid CreditAttribution: arif.stalker.majid commentedAny solution to this issue cuz its still happening ...
And all the options stated above
1. setting no cache (ajax file upload breaks)
2. using the after build option
seems to be incomplete in one way or the other
Comment #63
headbank CreditAttribution: headbank commentedSubscribin', for all the good it seems likely to do... This is ludicrous "design".
My own use-case is, I think, fairly simple compared to many of the above: I created, strictly per the D7 API docs, an AJAX-driven form where the values of the first couple of fields influence the contents of the third (a list of radios, built to a default config by the form-builder then regenerated via the
#ajax
callback on the other fields).All I'm looking to do is create a sane fallback mode for non-JS users. Based on the above and my own attempts, it appears the best I can do in the event of validation failure is to trigger
form_set_error()
and tell the user what they did wrong -- I can't actually reset the invalid fields to a sane value.Never mind adding *_form_alter() functions just for this purpose, why isn't this functionality supported in the validation function itself?
Words fail me.
Comment #64
rooby CreditAttribution: rooby commentedSeems like no one has properly looked into whether or not this also affects Drupal 8.
Making this a Drupal 8 issue until it is either fixed in D8 or confirmed that it isn't an issue in D8.
Comment #65
rooby CreditAttribution: rooby commented@headbank: The form_set_value() function can set form value from the validate function, although it sets it in $form_state['values'] so I'm not sure off the top of my head if that will end up back in your form on a validation error.
Comment #66
headbank CreditAttribution: headbank commented@rooby, Thanks for the suggestion, I'll look into it; however if all it does is set the
$form_state['values']
values then it won't work, because so far I've been changing those manually and I can see that they're not fed back into the form when it's reloaded (I think that$form_state
array passed into the validation function is just thrown away if validation has failed).I should add that I've also added
$form_state['rebuild'] = TRUE
and it made no difference.UPDATED TO ADD: From studying this thing of nightmare, it looks like $form_state should be preserved (saved back to the cache for re-rendering) "if cache is set and no_cache is no set and rebuild is not set" (at bottom middle in the chart). I'll try this; if that doesn't pan out it looks like I'll just have to make a multi-step, non-AJAX form instead. Which will be crap for everyone except the handful of people who might not have JS turned on.
UPDATED AGAIN TO ADD: The sanest, hack-free way I can find so far (albeit massively counterintuitive) is to not call
form_set_error()
in the validation function, but to instead set a custom flag in$form_state['foo']
that causes the submit function to bail and return to the form page (instead of the processing and redirect it performs on successful validation). As I also set$form_state['rebuild'] = TRUE
(or maybe this doesn't even matter, I dunno), I can also use $form_state to pass details of the errors back to the form-builder function.This still strikes me as a ridiculous way of doing things, though, and I'm no closer to being able to rationalise why, when my form is filled in incorrectly, the "Drupal Way" doesn't let me tweak the data in any meaningful way to return it to a sane state from which the user can continue, duly advised of what's wrong. I can either tell them what's wrong (using
form_set_error()
), or fix it for them (using the submit handler), not both. #DrupalWTF indeed.Comment #67
Alan D. CreditAttribution: Alan D. commented"so I'm not sure off the top of my head if that will end up back in your form on a validation error"
On validation with errors, the changes are lost.
Sadly, you need to hack things here. Possible to check $_POST / $_REQUEST, or start with globals or in the worst case (for the multistep if required) the session.
Note it is possible that the form token is altered between submit attempts, I remember getting caught out once with this, but the details escape me (i.e. $SESSIONS['wtf_drupal_form_cache'][$form_state['values']['token']] = $form_state['values']; failed) .
Comment #68
headbank CreditAttribution: headbank commentedHere is how I make corrections to rebuilt form values when validation has failed in D7. It relies on the undocumented array,
$form_state['input']
(these are the values that get loaded back into a rebuilt form).$form_state['rebuild'] = TRUE
but don't issue any calls to form_set_error().$form_state['fnord']
(or whatever) for access when rebuilding. Here you can also set a flag to tell your submit handler not to process the form.$form_state['input']
array (which is basically the $_POST or $_GET array) for inclusion in the rebuilt form.form_set_error()
can be simulated by usingdrupal_set_message()
for notifications, and highlighting errored fields with$form['myfield']['#attributes']['class'][] = 'error'
.Hope that helps somebody.
Comment #69
marcingy CreditAttribution: marcingy commentedAs per https://www.drupal.org/node/671574#comment-6466782 this by design a form is only built 'once'.
Comment #70
kristiaanvandeneyndeRespectfully, closing this issue like that is not the way to go. It's even incorrect to state that forms are only built once by design, because they aren't. They're only built once if caching is enabled.
It's plain to see that a lot of people are confused about some parts of their form alter not working consistently. At the very least we should update the documentation of hook_form_alter(), hook_form_FORM_ID_alter() and hook_form_BASE_FORM_ID_alter() to reflect that cached forms will not run their form alter code again.
While it is completely logical to not run the form alter again, we should at the minimum tell people about the available ways to circumvent this behavior: #attached, #after_build and perhaps #pre_render.
Comment #71
rooby CreditAttribution: rooby commentedAs per #49 #after_build is not always a viable solution.
Comment #72
ladybug_3777 CreditAttribution: ladybug_3777 commentedI just ran into this problem with some custom javascript I am adding to my form. My custom JS allows me to select an option from a drop down and pre-populate other drop downs on the form based on that first selection. I was using hook_form_node_form_alter for my drupal_add_js() call.
I agree with others that feel confused that the hook is only called once, and not if validation fails. I will need to try out some of the other suggestions (#attached, #after_build, etc).
Comment #73
ladybug_3777 CreditAttribution: ladybug_3777 commentedThe after_build path seems to be working for me so far. For those that are new to after-build remember you have to return $form (That messed me up for a few moments!)
Example:
Remember you must have the devel module if you are using dsm(); I added those in there for debugging as an example of when these functions are (and are not) run, they are not required of course
Comment #74
marcingy CreditAttribution: marcingy commentedIf you use #attached rather than drupal_add_js then you should no need to use yourmodulename_after_build as #attached ensures that all assets are re-added even when the form is rebuilt from cache.
Comment #75
ladybug_3777 CreditAttribution: ladybug_3777 commentedThanks so much marcingy! That seems to work great! It also seems to be a better solution because my after_build was being called on every ajax call so my javascript settings data was being added twice. I was actually struggling a bit with an array merge issue because of the ajax calls interaction with my JS settings data. #attached seems to solve that completely for me.
*edit* Well I guess technically the js is still re-added on ajax through attached, but the array settings issue went away at least.
Comment #76
AlexZt CreditAttribution: AlexZt commented#68 worked for me
The problem was in form_set_error()
I removed this function call with "form_validate"
and remove the option "#required" for all fields.
Comment #77
pratip.ghosh CreditAttribution: pratip.ghosh commented#73 was the problem for me. This is really a minute details that needs to be attended carefully.
Thanks
Comment #78
tim.plunkett+1
Comment #79
i.bajrai CreditAttribution: i.bajrai commented#74 "#attached" for javascript. drupal_add_js in the form_alter does not work after validation fails.
Comment #82
ann b CreditAttribution: ann b commentedI need to set a default value for a webworm dynamically, i.e. every time the form is rendered. I tried the following and none of them worked:
I know this issue is about form validation, but there was some discussion about hook_form_alter being cached and not executed every time the form is rendered. I understand this is by design. The only thing that has worked so far is to add drupal_page_is_cacheable(FALSE); in hook_form_alter after checking the form id. Without this function call, I saw the page getting cached in the cache_page table as a blob. So all page requests contain the default value calculated during the first request after clearing the cache. I still need to test this tomorrow.
Drupal 7.51
Using Drupal database caching system.
Comment #83
tim.plunkettThis is a D8 issue, drupal_page_is_cacheable() does not exist anymore.
Comment #84
Alan D. CreditAttribution: Alan D. commentedNo one has confirmed this is a D8 issue since 2014!
Comment #85
tim.plunkettThen that is the first step.
Comment #86
joseph.olstadsubscribing,
Scenario: Drupal 7.54 (patched with several rtbc patches), wrote a custom module for adding javascript on node edit form after validation and before validation.
Background:
I'm adding some validation to prompt a confirm popup when the publish checkbox is checked. I am able to add my javascript to the form for all the node types , however only some of my node types are getting my javascript after a form validation failure (for instance an empty title field where title field is 'required').
progress:
while using #attached helped for me for some of my node types to load my javascript after validation failure, I still have at least one node type (haven't tested all of them yet) that refuses to load my javascript after a validation failure. Suspect perhaps the difference between the node types is one has revisions enabled, the other one doesn't have revisions. The node edit form that has no revisions loads my javascript ok before and after validation (success or failure) , however the one that HAS revisions only loads my javascript prior to any validation failure ; and upon failure, my javascript is not loaded so my custom confirmation box doesn't show up.
Here's the add js
Here's my javascript code:
Comment #88
i.bajrai CreditAttribution: i.bajrai commentedI can confirm this is still happening in Drupal 8.
I am trying to add a custom form element to the page if validation fails. More specifically a checkbox that would stop the validation from failing if checked.
I cannot access anything useful in #afterbuild and the hook_form_alter is not called at all.
Comment #95
JohnAlbin> I can confirm this is still happening in Drupal 8.
That's weird. I just tried this on Drupal 8.9.9-dev and the form_alter is called just fine when form validation fails.
Steps to reproduce:
formdazzle_form_alter()
formdazzle_form_alter()
, and submit the form.formdazzle_form_alter()
. Hit the play button to let PHP continue executing.I'm moving this back to the Drupal 7 issue queue.
Comment #96
BerdirDrupal 8 is different because it only caches forms after the first ajax submission. The first ajax or regular submission rebuilds the form the first time, only repeated ajax calls validate the form against the cached version. Try to repeat your experiment by repeatedly using something like an image field/add more button.