Skip to content

Updates the REDCap Address Autocomplete #8

Open
274188A wants to merge 15 commits into
vanderbilt-redcap:masterfrom
274188A:master
Open

Updates the REDCap Address Autocomplete #8
274188A wants to merge 15 commits into
vanderbilt-redcap:masterfrom
274188A:master

Conversation

@274188A

@274188A 274188A commented Feb 13, 2026

Copy link
Copy Markdown

Overview

https://redcap.vumc.org/community/post.php?id=272337&comment=272419

Fix 1 — Null Reference Guard (lines 80-83)
The $(document).ready() block now checks whether the target autocomplete field exists on the current instrument before doing any initialization:

This eliminates the InvalidValueError: not an instance of HTMLInputElement on forms that don't contain the configured field.

Fix 2 — New Places API (PlaceAutocompleteElement) (lines 149-185) The deprecated google.maps.places.Autocomplete class has been replaced with the modern PlaceAutocompleteElement web component:

Creation: new PlaceAutocompleteElement({ types: ['address'] }) renders its own input widget, inserted into the DOM where the original REDCap field was (the original field is hidden but preserved for form submission). Event: Listens on gmp-placeselect instead of place_changed. Data fetch: Calls place.fetchFields({ fields: ['addressComponents', 'location', 'formattedAddress'] }) to retrieve details. Property mapping: A formatMap translates the legacy short_name/long_name keys to the new API's shortText/longText properties on each addressComponent. Geolocation bias uses placeAutocomplete.locationBias (new property) instead of autocomplete.setBounds().

Fix 3 — Async Script Loading (lines 36-39) The Google Maps script tag is now loaded asynchronously

Context

Screenshots

image

Copilot AI review requested due to automatic review settings February 13, 2026 23:25
The $(document).ready() block now checks whether the target autocomplete field exists on the current instrument before doing any initialization:

This eliminates the InvalidValueError: not an instance of HTMLInputElement on forms that don't contain the configured field.

Fix 2 — New Places API (PlaceAutocompleteElement) (lines 149-185)
The deprecated google.maps.places.Autocomplete class has been replaced with the modern PlaceAutocompleteElement web component:

Creation: new PlaceAutocompleteElement({ types: ['address'] }) renders its own input widget, inserted into the DOM where the original REDCap field was (the original field is hidden but preserved for form submission).
Event: Listens on gmp-placeselect instead of place_changed.
Data fetch: Calls place.fetchFields({ fields: ['addressComponents', 'location', 'formattedAddress'] }) to retrieve details.
Property mapping: A formatMap translates the legacy short_name/long_name keys to the new API's shortText/longText properties on each addressComponent.
Geolocation bias uses placeAutocomplete.locationBias (new property) instead of autocomplete.setBounds().

Fix 3 — Async Script Loading (lines 36-39)
The Google Maps script tag is now loaded asynchronously

@274188A 274188A left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same key, just flip on "Places API (New)" in Cloud Console.

Discalimer: code produced by Claude Opus 4.6

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the REDCap Address Autocomplete external module to avoid initializing on instruments that don’t contain the configured field, migrate to Google’s newer Places API UI component, and load the Google Maps script asynchronously to prevent runtime initialization errors.

Changes:

  • Add a guard in $(document).ready() to skip initialization when the target autocomplete field isn’t present on the current form.
  • Replace deprecated google.maps.places.Autocomplete with PlaceAutocompleteElement, updating event handling and address component mapping.
  • Load the Google Maps JS API asynchronously (when configured to import it).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread AddressExternalModule.php Outdated
Comment on lines +138 to +142
function initAutocomplete($field) {
waitForGoogleMaps()
.then(function() {
return google.maps.importLibrary('places');
})

Copilot AI Feb 13, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

waitForGoogleMaps() only checks for google.maps, but initAutocomplete() immediately calls google.maps.importLibrary('places'). In environments where the loaded Maps JS version doesn’t support importLibrary, initialization will always fail. Consider explicitly checking for google.maps.importLibrary (and/or ensuring the script URL requests a version that supports it) and either provide a fallback or surface a clearer error before attempting to call it.

Copilot uses AI. Check for mistakes.
Comment thread AddressExternalModule.php
Comment on lines +107 to +110
// Wrap original field and hide it; the PlaceAutocompleteElement will replace it visually
$autocompleteField.wrap('<div id="locationField"></div>');
$autocompleteField.hide();

Copilot AI Feb 13, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original REDCap autocomplete field is hidden and only updated on gmp-placeselect (or cleared when place is null). If a user types an address but does not select a suggestion, the typed value will not be copied into the hidden field, so form submission can lose what the user entered. Consider mirroring the component’s current text/value into the hidden $field on input/blur as well (and keep change() triggers), or avoid hiding the original field so manual entry is still submitted.

Suggested change
// Wrap original field and hide it; the PlaceAutocompleteElement will replace it visually
$autocompleteField.wrap('<div id="locationField"></div>');
$autocompleteField.hide();
// Wrap original field and hide it; the PlaceAutocompleteElement will replace it visually.
$autocompleteField.wrap('<div id="locationField"></div>');
$autocompleteField.hide();
// Mirror the visible autocomplete text into the hidden REDCap field on input/blur.
$('#locationField').on('input blur', '*', function(event) {
var value = $(this).val();
if (typeof value === 'string') {
$autocompleteField.val(value).trigger('change');
}
});

Copilot uses AI. Check for mistakes.
Comment thread AddressExternalModule.php Outdated
Comment on lines +118 to +130
function waitForGoogleMaps() {
return new Promise(function(resolve) {
if (typeof google !== 'undefined' && google.maps) {
resolve();
return;
}

var eleType = element.prop('type');
element.val(value);

// Is this a unique field type?
var eleName = element.attr('name');
if(element.hasClass('hiddenradio')) { // Are we working with a radio field?
$('input[name="'+eleName+'___radio"][value="'+value+'"]').prop('checked', true);
} else if(eleType.indexOf("select") >= 0) { // Is it a select field?
if($('#'+id+' option[value="'+value+'"]').length > 0) { // Is our value an option in the select?
$('#'+id+' option[value="'+value+'"]').prop('selected', true);
} else if($('#'+id+' option[value="'+valUnderscore+'"]').length > 0) { // Lets try again with underscores
var valUnderscore = value.replace(/\s+/g,"_");
console.log(valUnderscore);
var poll = setInterval(function() {
if (typeof google !== 'undefined' && google.maps) {
clearInterval(poll);
resolve();
}
}, 100);
});

Copilot AI Feb 13, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

waitForGoogleMaps() polls forever with no timeout/reject. If the Maps script fails to load (or import-google-api is unchecked and no other script provides google.maps), this leaves a permanent interval running and initAutocomplete() never completes. Add a timeout + rejection/cleanup path (and ideally skip init with a clear console error) to avoid infinite polling.

Copilot uses AI. Check for mistakes.
Legacy Autocomplete is tried first — this is what your existing Google Cloud project has enabled (the standard "Places API").
New PlaceAutocompleteElement is only used when the legacy class doesn't exist at all — which only happens for brand-new post-March-2025 accounts that exclusively have "Places API (New)".
This should resolve the AutocompletePlaces are blocked error immediately without any Cloud Console changes. Reload the page and the dropdown should work.
@274188A 274188A changed the title Master Updates the REDCap Address Autocomplete Feb 15, 2026
274188A and others added 3 commits April 4, 2026 11:29
Introduce a new project setting `place-name` (config.json) to track a separate Place Name field. AddressExternalModule.php now reads this setting, assigns an id to the original field, disables/enables it alongside lat/lng fields, and updates/clears its value in the autocomplete success/failure paths. Also extend fetched Place fields to include `displayName` and handle both `place.name` and `place.displayName` where appropriate so the place name is preserved when a user selects or clears an autocomplete result.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants