1/** 2 * Ce contrôleur Stimulus permet de gérer l'autocomplétion des champs d'adresse 3 * et de les remplir automatiquement à l'aide de l'API Google Maps Places. 4 * 5 * Il gère également l'affichage ou la masquage des champs de saisie manuelle en cas d'absence 6 * de correspondance trouvée par Google Maps. 7 * 8 * Champs requis : 9 * - Les champs pour le "n° et nom de rue", "code postal", "ville", "pays" doivent avoir les attributs Stimulus suivants : 10 * - `data-google-map-autocomplete-target="googleAutocomplete"` pour être ciblés par le contrôleur. 11 * 12 * - Le champ de recherche Google doit avoir les attributs suivants : 13 * - `name="googleAutocomplete"` et `id="address_googleMaps"`, ainsi que le target Stimulus correspondant pour l'autocomplétion : 14 * `data-google-map-autocomplete-target="autoInput"`. 15 * 16 * - Les champs à cacher se trouvent dans une div avec `data-google-map-autocomplete-target="manuelAdresse"` et la classe `d-none` par défaut. 17 * - Ils seront affichés lorsque l'utilisateur clique sur le bouton pour saisir une adresse manuellement. 18 * 19 * - Le bouton pour afficher les champs d'adresse manuels doit se trouver dans une div avec `data-google-map-autocomplete-target="noAutoAdresse"` 20 * et la classe `d-none`. Ce bouton doit appeler l'action Stimulus `displayAddress`. 21 * 22 * A noter : 23 * - Si le champs de recherche d'adresse est géré via le bundle ivory, passer au controller le parametre `isIvoryUsed` à true pour ne pas l'initialiser ici. 24 */ 25 26 27import { Controller } from '@hotwired/stimulus'; 28 29export default class extends Controller { 30 static values = { 31 isIvoryUsed: Boolean 32 } 33 static targets = [ 34 "manuelAdresse", 35 "noAutoAdresse", 36 "googleAutocomplete", 37 "buttonAdresseManuel", 38 "autoInput" 39 ]; 40 41 connect() { 42 if (!this.isIvoryUsedValue) { 43 this.initializeGoogleAutocomplete(); 44 } 45 46 this.handleFocusOut = this.handleFocusOut.bind(this); 47 this.alreadySearch = false; 48 49 document.getElementById('address_googleMaps').addEventListener('focusout', this.handleFocusOut); 50 51 if (this.fieldsAreNotEmpty()) { 52 this.displayAddress(); 53 this.alreadySearch = true 54 55 } 56 } 57 58 initializeGoogleAutocomplete() { 59 const autocomplete = new google.maps.places.Autocomplete(this.autoInputTarget, {}); 60 61 autocomplete.addListener("place_changed", () => { 62 const place = autocomplete.getPlace(); 63 this.fillInAddress(place); 64 }); 65 } 66 67 fillInAddress(place) { 68 if (place) { 69 let street_number = this.extractFromAddress(place.address_components, "street_number"); 70 let street = this.extractFromAddress(place.address_components, "route"); 71 let postCode = this.extractFromAddress(place.address_components, "postal_code"); 72 let town = this.extractFromAddress(place.address_components, "locality"); 73 let country = this.extractFromAddress(place.address_components, "country"); 74 75 this.googleAutocompleteTargets.forEach(target => { 76 const fieldName = target.name; 77 78 if (fieldName.includes('adresse')) { 79 target.value = [street_number, street].join(" "); 80 } 81 82 if (fieldName.includes('codePostal')) { 83 target.value = postCode; 84 } 85 86 if (fieldName.includes('ville')) { 87 target.value = town; 88 } 89 90 if (fieldName.includes('pays')) { 91 target.value = country; 92 } 93 }); 94 95 this.displayAddress(); 96 } 97 } 98 99 extractFromAddress(components, type) { 100 for (let i = 0; i < components.length; i++) { 101 if (components[i].types.includes(type)) { 102 return components[i].long_name; 103 } 104 } 105 return ''; 106 } 107 108 handleFocusOut() { 109 setTimeout(() => { 110 if (this.fieldsAreNotEmpty()) { 111 this.displayAddress(); 112 } else { 113 this.noAutoAdresseTarget.classList.remove('d-none'); 114 } 115 }, 200); 116 } 117 118 removeFocusOutListener() { 119 document.getElementById('address_googleMaps').removeEventListener('focusout', this.handleFocusOut); 120 } 121 122 displayAddress() { 123 this.manuelAdresseTarget.classList.remove('d-none'); 124 this.noAutoAdresseTarget.classList.add('d-none'); 125 this.removeFocusOutListener(); 126 } 127 128 fieldsAreNotEmpty() { 129 return this.googleAutocompleteTargets.some(target => target.value.trim() !== ''); 130 } 131}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.