When a dependent element is added to a form via AJAX, #states works as expected (at least in simple cases) when the triggering element (dependee) is inside the AJAX wrapper. If the dependee is outside the wrapper however, the dependent is not properly initialized and keeps its default state until the dependee changes.
To reproduce, download and enable the attached module derived from the examples project. This module adds a dependent "First Name" textfield via AJAX. The dependee is the checkbox "Disable first name" outside the "textfields" wrapper.
Then, visit scratch/states_issue
- Check "Ask me my first name"
- The "First Name" textfield appears
- Check "Disable first name textfield"
- The "First Name" textfield is now, as expected, disabled
Revisit scratch/states_issue
- Check "Disable first name textfield"
- Check "Ask me my first name"
- The "First Name" textfield appears. Expected state: disabled. Actual state: enabled.
Toggling the "Disable first name textfield" box will update the "First Name" textfield correctly. It's just the triggering upon ajax completion that appears to not work as *I* expect.
Comment | File | Size | Author |
---|---|---|---|
#28 | fapi_states_dependent-2226405-28.patch | 952 bytes | jibran |
Comments
Comment #1
nod_Leaving on D7 for now.
Comment #2
nod_IMO it's more of a UX issue but there is indeed a bug. In the sample module, the #states behavior is initialized several times and pretty much confuse itself with default values and all.
Applying the patch from #1592688-32: #states can cause the form "required" mark to appear more than once on the same element to states.js should solve your issue. seems like it soved it for me in any case. It'd be great if you could roll the patch for the other issue :)
Comment #3
Heine CreditAttribution: Heine commentedThanks!
#1592688: #states can cause the form "required" mark to appear more than once on the same element provided real progress and uncovered another issue that's already hinted at in the former issue, but I'll split this off here, thus reopening
The use case demonstrated by the scratch example module is one where the user checks the Dependee, only to obtain, or remove the Dependent, AJAX loaded fields later.
With the patch in #1592688-42: #states can cause the form "required" mark to appear more than once on the same element, the following workflow works:
- Check Dependee
- Check Ask me my first name (this also loads states.js)
- The Dependent is disabled as expected
Now, stuff breaks down if the field is removed:
- Uncheck Ask me my first name
- Check Ask me my first name
- The Dependent is enabled (expected: disabled).
This happens because the Dependee has a data flag set that indicates it already been initialized.
Possible solutions:
Comment #4
lmeurs CreditAttribution: lmeurs commentedOur problem seems similar: we have a custom form with two managed file fields (which upload files using AJAX) and a text field. When either of the managed file fields has a file selected, the text field should appear. Though the text field does appear, after removing both files it does not disappear.
We use a
visible
state withOR
condition that checks whether one of the managed files' values is a file ID other than0
:First time
Drupal.behaviors.states.attach
is calledA data flag that is set on dependees that indicates the dependee has been initialized. On initialization amongst other things an event is bound to the dependee, but also it's value is being cached at
DrupalStatesDependentInstance.values[selector][stateName]
.Second time
Drupal.behaviors.states.attach
is calledAfter ie. uploading a file
Drupal.behaviors.states.attach
is called again.Data flags have already been set on existing dependees, so they are not being initialized again. This prevents events from being bound twice, but also causes their values cached at
DrupalStatesDependentInstance.values[selector][stateName]
to remain at their initial valuesnull
as set instates.Dependent.prototype.initializeDependee()
.Newly inserted elements by AJAX do not have the data flag set and are getting initialized.
Back to our example
After a file upload
Drupal.behaviors.states.attach
is called for the second time. The cached values of both managed file fields are initially set tonull
. The existing managed file field already had the data flag and is not being initialized, so it's cached value remainsnull
and according to our state's conditionsnull !== '0'
the text field wrongly(!) stays visible.Data flag
The data flag is being used to prevent double events being bound to a dependee, the comments state:
but this also causes the values cache not being initialized properly.
If the sole purpose is preventing double bound events, could we not A) remove this data flag completely and B) unbind possible earlier bound events before binding new ones?
In
Drupal.states.Trigger
(unpatched Drupal 7.28) I commented out the if statement (though javascript, I use PHP formatting for syntax highlighting):and in
Drupal.states.prototype.defaultTrigger
I commented out the old event binding and added a new line that first unbinds possible events using event namespacing:I am pretty familiar with js / jQuery, the states script is pretty hard to get my head around. This fix works for me so far, does it help you?
Comment #5
jgalletta CreditAttribution: jgalletta commentedThe solution proposed by lmeurs corrects the issue in my case.
Here's a patch corresponding to his modifications.
Comment #6
David_Rothstein CreditAttribution: David_Rothstein commentedComment #7
larowlanPatch at #5 fixes similar issues encountered, #states not applied to dependant elements returned in an #ajax callback
Comment #8
lmeurs CreditAttribution: lmeurs commentedThanks for the patch and the review! If this is the way to go, than:
'trigger:' + this.state
data variable,See patch attached.
Comment #9
rooby CreditAttribution: rooby commentedRelated issue #1091852: Display Bug when using #states (Forms API) with Ajax Request
Comment #10
DimitriV CreditAttribution: DimitriV as a volunteer and at Mia Interactive commentedThe same problem exists in Drupal 8.0.X. I updated the patch against 8.0.X
Comment #11
David_Rothstein CreditAttribution: David_Rothstein commentedComment #12
radiancebox CreditAttribution: radiancebox commentedThe scratch.tar.gz is a 7 module.
Comment #13
m.lebedev CreditAttribution: m.lebedev commented$dependee can be empty also...
Comment #15
DimitriV CreditAttribution: DimitriV as a volunteer and at Mia Interactive commentedWe need first to fix the issues in D8 before we can start backporting. The test will fail because the issue is tagged to D8.
Comment #17
droplet CreditAttribution: droplet commentedComment #18
Eyal ShalevI fixed the issue by adding a behavior detach function to the states object, that will set the
this.element.data('trigger:' + this.state)
tofalse
.Comment #19
Eyal ShalevComment #23
filsterjisah CreditAttribution: filsterjisah for District09 commentedPatch from #19 breaks javascript (missing ",").
Comment #24
woprrr CreditAttribution: woprrr as a volunteer and at NeoLynk commentedThis patch fixe the problem about duplicate call of Ajax when we trigger "add_more" button. This possibly fixe your problem too. I don't provide an interdiff because the previous patch is to D7 and break D8.
Comment #26
Tom Robert CreditAttribution: Tom Robert commented#24 does what it needs to.
An ajax replace with elements which contains state condition resulted in losing het information for the dependent elements.
This patched fixed it.
*edit* I mis-tested it, the fields don't disappear anymore, but there are not updated when the element changes
Comment #27
Tom Robert CreditAttribution: Tom Robert commentedComment #28
jibranHere is a reroll with es6 file change as well.
Comment #29
kfritscheClosing this issue as its a duplicate of #1091852: Display Bug when using #states (Forms API) with Ajax Request