Steps to reproduce:
- Visit a form that allows to edit an address field.
- I first encountered it on the commerce checkout form, but it also happens when editing a commerce shipping profile.
- Use browser autocomplete (Chromium) to fill in an address where the country is different than whichever country is preselected.
Expected behavior:
All fields are filled in as from the browser autocomplete.
Actual behavior:
- Fields are filled in correctly.
- Ajax reload triggers
- Fields are wiped empty, except the country.
Technical explanation
Two effects:
- The ajax request is sent before all the form elements are filled in by the browser autocomplete.
- Even if the recent form data would be sent with the ajax request, addressfield_standard_country_validate() still clears the city and postal code (dependent_locality, locality, administrative_area, postal_code), if country has changed.
Solution
Two changes:
- Add a small delay (e.g. one millisecond) before firing the ajax request, to allow the browser autofill to complete before collecting form data for ajax.
- Somehow prevent the reset of form elements that were (recently) modified on client side.
Implementation
I don't know. Does Drupal #ajax support adding a delay like this?
Comments
Comment #2
donquixote CreditAttribution: donquixote commentedI don't see a straightforward solution with Drupal #ajax.
So here is an idea:
Define a new event name "change_delayed".
Bind a "change" behavior to country field, using custom js file, which waits a microsecond and then fires the "change_delayed" event.
Register the "#ajax" behavior with "event" => "change_delayed".
This allows to have our delay without reinventing all the Drupal ajax mechanics.
Comment #3
donquixote CreditAttribution: donquixote commentedA different idea I had before was to create an additional country element which copies the value of country with a delay, and then register the #ajax behavior on this delayed country field. But I think this would add unnecessary clutter.
Comment #4
donquixote CreditAttribution: donquixote commentedI played around with delay as in #2.
It does preserve the street and name values.
However, the postal code and city are wiped empty.
The $_POST does contain the complete address including postal code and city.
However,
$form_state['input']['commerce_customer_address']['und'][0]
only contains thoroughfare, premise, country, organisation_name and name_line.This means an ajax delay can only be a half-solution.
Comment #5
donquixote CreditAttribution: donquixote commentedSo, turns out it is addressfield itself which clears the postal code and city elements.
In addressfield_standard_country_validate():
Comment #6
donquixote CreditAttribution: donquixote commentedAnother idea would be to remember values on client side.
Comment #7
donquixote CreditAttribution: donquixote commentedI wonder if the browser autocomplete always fills the country first?
Comment #8
donquixote CreditAttribution: donquixote commentedAnother look at addressfield_standard_country_validate():
The function checks whether the #default_value for country is different from #value for country.
Maybe it should do the equivalent check for other elements:
Only reset those fields where #default_value and #value are the same.
This would be in addition to the ajax delay.
Comment #9
donquixote CreditAttribution: donquixote commentedProblem: How do we determine the #default_value of those fields, from within addressfield_standard_country_validate()?
Maybe instead we should register a validation handler on a parent element, so that it has access to each child element's #default_value?
Can we assume that the array structure is consistent?
Next problem: Even if
$element['locality']['#default_value'] === $element['locality']['#value']
, we cannot be sure that this postal code has to be deleted when changing the country.Thanks to addressfield_field_widget_form() calling
$input_address = drupal_array_get_nested_value($form_state['input'], $parents);
the #default_value is not the original value, but just the previous state of the form.E.g. what happens if the user fills in the other fields before changing the country?
Usual case:
- User loads page with country = NL, locality = Amsterdam
- User sets locality = Berlin
- User sets country = DE
- Ajax request fires, finds that country and locality have changed -> so nothing is emptied, which is good.
But:
- User loads page with country = NL, locality = Amsterdam
- User sets locality = Berlin
- Some other ajax request happens, which sets [locality][#default_value] = Berlin
- User sets country = DE
- Ajax request fires, finds that country has changed, but locality is still the same -> locality is emptied, which is not good.
Maybe this is acceptable and to be expected, as long as the country change was initiated manually by the user?
(as opposed to a browser autofill)
Comment #10
donquixote CreditAttribution: donquixote commentedComment #11
donquixote CreditAttribution: donquixote commented