Place Autocomplete Data API מאפשר לכם לאחזר תחזיות של מקומות באופן פרוגרמטי, כדי ליצור חוויות השלמה אוטומטית מותאמות אישית עם רמת שליטה גבוהה יותר מזו שאפשרה ווידג'ט ההשלמה האוטומטית. במדריך הזה נסביר איך להשתמש ב-Place Autocomplete Data API כדי לשלוח בקשות להשלמה אוטומטית על סמך שאילתות של משתמשים.
בדוגמה הבאה מוצג שילוב פשוט של השלמה אוטומטית. מזינים את שאילתת החיפוש, למשל 'פיצה' או 'פוקי', ואז לוחצים על התוצאה הרצויה כדי לבחור בה.
בקשות להשלמה אוטומטית
בקשה להשלמה אוטומטית מקבלת מחרוזת קלט של שאילתה ומחזירה רשימה של חיזויים של מקומות. כדי לשלוח בקשה להשלמה אוטומטית, קוראים ל-fetchAutocompleteSuggestions()
ומעבירים בקשה עם המאפיינים הנדרשים. המאפיין input
מכיל את המחרוזת לחיפוש. בדרך כלל, הערך הזה מתעדכן כשהמשתמש מקליד שאילתה. הבקשה צריכה לכלול sessionToken
, שמשמש למטרות חיוב.
בקטע הקוד הבא מוצג תהליך של יצירת גוף בקשה והוספה של אסימון סשן, ואז קריאה ל-fetchAutocompleteSuggestions()
כדי לקבל רשימה של PlacePrediction
.
// Add an initial request body. let request = { input: "Tadi", locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78, }, origin: { lat: 37.7893, lng: -122.4039 }, includedPrimaryTypes: ["restaurant"], language: "en-US", region: "us", }; // Create a session token. const token = new AutocompleteSessionToken(); // Add the token to the request. // @ts-ignore request.sessionToken = token;
הגבלת ההצעות להשלמה אוטומטית
כברירת מחדל, השלמה אוטומטית של מקומות מציגה את כל סוגי המקומות, עם הטיה לתחזיות לגבי מקומות בקרבת המיקום של המשתמש, ומאחזרת את כל שדות הנתונים הזמינים לגבי המקום שהמשתמש בחר. הגדרת אפשרויות השלמה אוטומטית של מקומות כדי להציג חיזויים רלוונטיים יותר, על ידי הגבלת התוצאות או הטייתן.
הגבלת התוצאות גורמת לווידג'ט ההשלמה האוטומטית להתעלם מתוצאות שנמצאות מחוץ לאזור ההגבלה. נהוג להגביל את התוצאות לגבולות המפה. הטיה של תוצאות גורמת לווידג'ט של ההשלמה האוטומטית להציג תוצאות באזור שצוין, אבל יכול להיות שחלק מההתאמות יהיו מחוץ לאזור הזה.
משתמשים במאפיין origin
כדי לציין את נקודת המוצא שממנה יחושב המרחק הגיאודזי אל היעד. אם לא מציינים את הערך הזה, המרחק לא מוחזר.
משתמשים במאפיין includedPrimaryTypes
כדי לציין עד חמישה סוגי מקומות.
אם לא מציינים סוגים, יוחזרו מקומות מכל הסוגים.
קבלת פרטי מקום
כדי להחזיר אובייקט Place
מתוצאת חיזוי של מקום, קודם קוראים ל-toPlace()
,
ואז קוראים ל-fetchFields()
באובייקט Place
שמתקבל (מזהה הסשן מחיזוי המקום נכלל באופן אוטומטי). התקשרות אל fetchFields()
מסיימת את סשן ההשלמה האוטומטית.
let place = suggestions[0].placePrediction.toPlace(); // Get first predicted place. await place.fetchFields({ fields: ["displayName", "formattedAddress"], }); const placeInfo = document.getElementById("prediction"); placeInfo.textContent = "First predicted place: " + place.displayName + ": " + place.formattedAddress;
טוקנים של סשנים
אסימוני סשן מקבצים את שלבי השאילתה והבחירה של חיפוש השלמה אוטומטית של משתמש לסשן נפרד למטרות חיוב. הסשן מתחיל כשהמשתמש מתחיל להקליד. הסשן מסתיים כשהמשתמש בוחר מקום ומתבצעת קריאה לפרטי המקום.
כדי ליצור טוקן חדש של סשן ולהוסיף אותו לבקשה, יוצרים מופע של AutocompleteSessionToken
, ואז מגדירים את המאפיין sessionToken
של הבקשה לשימוש בטוקנים כמו שמוצג בקטע הקוד הבא:
// Create a session token. const token = new AutocompleteSessionToken(); // Add the token to the request. // @ts-ignore request.sessionToken = token;
סשן מסתיים כשמתבצעת קריאה אל fetchFields()
. אחרי שיוצרים את מופע Place
, אין צורך להעביר את טוקן הסשן אל fetchFields()
כי המערכת מטפלת בזה באופן אוטומטי.
await place.fetchFields({ fields: ["displayName", "formattedAddress"], });
יוצרים טוקן סשן לסשן הבא על ידי יצירת מופע חדש של AutocompleteSessionToken
.
המלצות לטוקן סשן:
- מומלץ להשתמש באסימוני סשן לכל הקריאות ל-Place Autocomplete.
- צריך ליצור טוקן חדש לכל סשן.
- צריך להעביר טוקן ייחודי של סשן לכל סשן חדש. אם משתמשים באותו אסימון ליותר מסשן אחד, כל בקשה תחויב בנפרד.
אפשר להשמיט את טוקן הסשן של ההשלמה האוטומטית מבקשה. אם לא מציינים את טוקן הסשן, כל בקשה מחויבת בנפרד, והשימוש במק"ט השלמה אוטומטית – לכל בקשה מופעל. אם משתמשים מחדש בטוקן סשן, הסשן נחשב לא תקף והחיוב על הבקשות מתבצע כאילו לא סופק טוקן סשן.
דוגמה
בזמן שהמשתמש מקליד שאילתה, נשלחת בקשה להשלמה אוטומטית אחרי כל כמה הקשות (לא אחרי כל תו), ומוחזרת רשימה של תוצאות אפשריות. כשמשתמש בוחר משהו מרשימת התוצאות, הבחירה נחשבת כבקשה, וכל הבקשות שנשלחות במהלך החיפוש מקובצות יחד ונספרות כבקשה אחת. אם המשתמש בוחר מקום, שאילתת החיפוש זמינה ללא תשלום, ורק בקשת הנתונים של המקום מחויבת. אם המשתמש לא בוחר תוך כמה דקות מתחילת הסשן, הוא יחויב רק על שאילתת החיפוש.
מנקודת המבט של אפליקציה, רצף האירועים הוא כזה:
- משתמש מתחיל להקליד שאילתה כדי לחפש את 'פריז, צרפת'.
- כשמזוהה קלט משתמש, האפליקציה יוצרת אסימון סשן חדש, Token A.
- בזמן שהמשתמש מקליד, ה-API שולח בקשה להשלמה אוטומטית אחרי כל כמה תווים, ומציג רשימה חדשה של תוצאות אפשריות לכל אחד מהם:
'P'
'Par'
'Paris'
'Paris, Fr'
- כשהמשתמש בוחר אפשרות:
- כל הבקשות שנובעות מהשאילתה מקובצות ונוספות לסשן שמיוצג על ידי Token A, כבקשה אחת.
- הבחירה של המשתמש נספרת כבקשה לפרטי מקום, והיא מתווספת לסשן שמיוצג על ידי Token A.
- הסשן מסתיים, והאפליקציה מבטלת את האסימון A.
קוד לדוגמה מלא
בקטע הזה מופיעות דוגמאות מלאות שממחישות איך להשתמש ב-Place Autocomplete Data API .הצעות להשלמת החיפוש של מקומות
בדוגמה הבאה מוצגת קריאה ל-fetchAutocompleteSuggestions()
עבור הקלט Tadi, ואז קריאה ל-toPlace()
על תוצאת החיזוי הראשונה, ואחריה קריאה ל-fetchFields()
כדי לקבל פרטים על המקום.
TypeScript
/** * Demonstrates making a single request for Place predictions, then requests Place Details for the first result. */ async function init() { // @ts-ignore const { Place, AutocompleteSessionToken, AutocompleteSuggestion } = await google.maps.importLibrary("places") as google.maps.PlacesLibrary; // Add an initial request body. let request = { input: "Tadi", locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78 }, origin: { lat: 37.7893, lng: -122.4039 }, includedPrimaryTypes: ["restaurant"], language: "en-US", region: "us", }; // Create a session token. const token = new AutocompleteSessionToken(); // Add the token to the request. // @ts-ignore request.sessionToken = token; // Fetch autocomplete suggestions. const { suggestions } = await AutocompleteSuggestion.fetchAutocompleteSuggestions(request); const title = document.getElementById('title') as HTMLElement; title.appendChild(document.createTextNode('Query predictions for "' + request.input + '":')); for (let suggestion of suggestions) { const placePrediction = suggestion.placePrediction; // Create a new list element. const listItem = document.createElement('li'); const resultsElement = document.getElementById("results") as HTMLElement; listItem.appendChild(document.createTextNode(placePrediction.text.toString())); resultsElement.appendChild(listItem); } let place = suggestions[0].placePrediction.toPlace(); // Get first predicted place. await place.fetchFields({ fields: ['displayName', 'formattedAddress'], }); const placeInfo = document.getElementById("prediction") as HTMLElement; placeInfo.textContent = 'First predicted place: ' + place.displayName + ': ' + place.formattedAddress; } init();
JavaScript
/** * Demonstrates making a single request for Place predictions, then requests Place Details for the first result. */ async function init() { // @ts-ignore const { Place, AutocompleteSessionToken, AutocompleteSuggestion } = await google.maps.importLibrary("places"); // Add an initial request body. let request = { input: "Tadi", locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78, }, origin: { lat: 37.7893, lng: -122.4039 }, includedPrimaryTypes: ["restaurant"], language: "en-US", region: "us", }; // Create a session token. const token = new AutocompleteSessionToken(); // Add the token to the request. // @ts-ignore request.sessionToken = token; // Fetch autocomplete suggestions. const { suggestions } = await AutocompleteSuggestion.fetchAutocompleteSuggestions(request); const title = document.getElementById("title"); title.appendChild( document.createTextNode('Query predictions for "' + request.input + '":'), ); for (let suggestion of suggestions) { const placePrediction = suggestion.placePrediction; // Create a new list element. const listItem = document.createElement("li"); const resultsElement = document.getElementById("results"); listItem.appendChild( document.createTextNode(placePrediction.text.toString()), ); resultsElement.appendChild(listItem); } let place = suggestions[0].placePrediction.toPlace(); // Get first predicted place. await place.fetchFields({ fields: ["displayName", "formattedAddress"], }); const placeInfo = document.getElementById("prediction"); placeInfo.textContent = "First predicted place: " + place.displayName + ": " + place.formattedAddress; } init();
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; }
HTML
<html> <head> <title>Place Autocomplete Data API Predictions</title> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="title"></div> <ul id="results"></ul> <p><span id="prediction"></span></p> <img class="powered-by-google" src="https://storage.googleapis.com/geo-devrel-public-buckets/powered_by_google_on_white.png" alt="Powered by Google" /> <!-- prettier-ignore --> <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))}) ({key: "AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg", v: "weekly"});</script> </body> </html>
דוגמה לניסיון
השלמה אוטומטית של מקומות עם חיפוש תוך כדי הקלדה באמצעות סשנים
בדוגמה הזו אפשר לראות את המושגים הבאים:
- התקשרות אל
fetchAutocompleteSuggestions()
על סמך שאילתות של משתמשים והצגת רשימה של מקומות חזויים בתגובה. - שימוש באסימוני סשן כדי לקבץ שאילתת משתמש עם בקשת הפרטים הסופית של המקום.
- שליפת פרטי המקום שנבחר והצגת סמן.
- שימוש ב-control slotting כדי להטמיע רכיבי ממשק משתמש ברכיב
gmp-map
.
TypeScript
const mapElement = document.querySelector('gmp-map') as google.maps.MapElement; let innerMap: google.maps.Map; let marker: google.maps.marker.AdvancedMarkerElement; let titleElement = document.querySelector('.title') as HTMLElement; let resultsContainerElement = document.querySelector('.results') as HTMLElement; let inputElement = document.querySelector('input') as HTMLInputElement; let tokenStatusElement = document.querySelector('.token-status') as HTMLElement; let newestRequestId = 0; let tokenCount = 0; // Create an initial request body. const request: google.maps.places.AutocompleteRequest = { input: '', includedPrimaryTypes: [ 'restaurant', 'cafe', 'museum', 'park', 'botanical_garden', ], } async function init() { await google.maps.importLibrary('maps'); innerMap = mapElement.innerMap; innerMap.setOptions({ mapTypeControl: false, }); // Update request center and bounds when the map bounds change. google.maps.event.addListener(innerMap, 'bounds_changed', async () => { request.locationRestriction = innerMap.getBounds(); request.origin = innerMap.getCenter(); }); inputElement.addEventListener('input', makeAutocompleteRequest); } async function makeAutocompleteRequest(inputEvent) { // To avoid race conditions, store the request ID and compare after the request. const requestId = ++newestRequestId; const { AutocompleteSuggestion } = (await google.maps.importLibrary( 'places' )) as google.maps.PlacesLibrary; if (!inputEvent.target?.value) { titleElement.textContent = ''; resultsContainerElement.replaceChildren(); return; } // Add the latest char sequence to the request. request.input = (inputEvent.target as HTMLInputElement).value; // Fetch autocomplete suggestions and show them in a list. const { suggestions } = await AutocompleteSuggestion.fetchAutocompleteSuggestions(request); // If the request has been superseded by a newer request, do not render the output. if (requestId !== newestRequestId) return; titleElement.innerText = `Place predictions for "${request.input}"`; // Clear the list first. resultsContainerElement.replaceChildren(); for (const suggestion of suggestions) { const placePrediction = suggestion.placePrediction; if (!placePrediction) { continue; } // Create a link for the place, add an event handler to fetch the place. // We are using a button element to take advantage of its a11y capabilities. const placeButton = document.createElement('button'); placeButton.addEventListener('click', () => { onPlaceSelected(placePrediction.toPlace()); }); placeButton.textContent = placePrediction.text.toString(); placeButton.classList.add('place-button'); // Create a new list item element. const li = document.createElement('li'); li.appendChild(placeButton); resultsContainerElement.appendChild(li); } } // Event handler for clicking on a suggested place. async function onPlaceSelected(place: google.maps.places.Place) { const { AdvancedMarkerElement } = (await google.maps.importLibrary( 'marker' )) as google.maps.MarkerLibrary; await place.fetchFields({ fields: ['displayName', 'formattedAddress', 'location'], }); resultsContainerElement.textContent = `${place.displayName}: ${place.formattedAddress}`; titleElement.textContent = 'Selected Place:'; inputElement.value = ''; await refreshToken(); // Remove the previous marker, if it exists. if (marker) { marker.remove(); } // Create a new marker. marker = new AdvancedMarkerElement({ map: innerMap, position: place.location, title: place.displayName, }) // Center the map on the selected place. if (place.location) { innerMap.setCenter(place.location); innerMap.setZoom(15); } } // Helper function to refresh the session token. async function refreshToken() { const { AutocompleteSessionToken } = (await google.maps.importLibrary( 'places' )) as google.maps.PlacesLibrary; // Increment the token counter. tokenCount++; // Create a new session token and add it to the request. request.sessionToken = new AutocompleteSessionToken(); tokenStatusElement.textContent = `Session token count: ${tokenCount}`; } init();
JavaScript
const mapElement = document.querySelector('gmp-map'); let innerMap; let marker; let titleElement = document.querySelector('.title'); let resultsContainerElement = document.querySelector('.results'); let inputElement = document.querySelector('input'); let tokenStatusElement = document.querySelector('.token-status'); let newestRequestId = 0; let tokenCount = 0; // Create an initial request body. const request = { input: '', includedPrimaryTypes: [ 'restaurant', 'cafe', 'museum', 'park', 'botanical_garden', ], }; async function init() { await google.maps.importLibrary('maps'); innerMap = mapElement.innerMap; innerMap.setOptions({ mapTypeControl: false, }); // Update request center and bounds when the map bounds change. google.maps.event.addListener(innerMap, 'bounds_changed', async () => { request.locationRestriction = innerMap.getBounds(); request.origin = innerMap.getCenter(); }); inputElement.addEventListener('input', makeAutocompleteRequest); } async function makeAutocompleteRequest(inputEvent) { // To avoid race conditions, store the request ID and compare after the request. const requestId = ++newestRequestId; const { AutocompleteSuggestion } = (await google.maps.importLibrary('places')); if (!inputEvent.target?.value) { titleElement.textContent = ''; resultsContainerElement.replaceChildren(); return; } // Add the latest char sequence to the request. request.input = inputEvent.target.value; // Fetch autocomplete suggestions and show them in a list. const { suggestions } = await AutocompleteSuggestion.fetchAutocompleteSuggestions(request); // If the request has been superseded by a newer request, do not render the output. if (requestId !== newestRequestId) return; titleElement.innerText = `Place predictions for "${request.input}"`; // Clear the list first. resultsContainerElement.replaceChildren(); for (const suggestion of suggestions) { const placePrediction = suggestion.placePrediction; if (!placePrediction) { continue; } // Create a link for the place, add an event handler to fetch the place. // We are using a button element to take advantage of its a11y capabilities. const placeButton = document.createElement('button'); placeButton.addEventListener('click', () => { onPlaceSelected(placePrediction.toPlace()); }); placeButton.textContent = placePrediction.text.toString(); placeButton.classList.add('place-button'); // Create a new list item element. const li = document.createElement('li'); li.appendChild(placeButton); resultsContainerElement.appendChild(li); } } // Event handler for clicking on a suggested place. async function onPlaceSelected(place) { const { AdvancedMarkerElement } = (await google.maps.importLibrary('marker')); await place.fetchFields({ fields: ['displayName', 'formattedAddress', 'location'], }); resultsContainerElement.textContent = `${place.displayName}: ${place.formattedAddress}`; titleElement.textContent = 'Selected Place:'; inputElement.value = ''; await refreshToken(); // Remove the previous marker, if it exists. if (marker) { marker.remove(); } // Create a new marker. marker = new AdvancedMarkerElement({ map: innerMap, position: place.location, title: place.displayName, }); // Center the map on the selected place. if (place.location) { innerMap.setCenter(place.location); innerMap.setZoom(15); } } // Helper function to refresh the session token. async function refreshToken() { const { AutocompleteSessionToken } = (await google.maps.importLibrary('places')); // Increment the token counter. tokenCount++; // Create a new session token and add it to the request. request.sessionToken = new AutocompleteSessionToken(); tokenStatusElement.textContent = `Session token count: ${tokenCount}`; } init();
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } .place-button { height: 3rem; width: 100%; background-color: transparent; text-align: left; border: none; cursor: pointer; } .place-button:focus-visible { outline: 2px solid #0056b3; border-radius: 2px; } .input { width: 300px; font-size: small; margin-bottom: 1rem; } /* Styles for the floating panel */ .controls { background-color: #fff; border-radius: 8px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); font-family: sans-serif; font-size: small; margin: 12px; padding: 1rem; } .title { font-weight: bold; margin-top: 1rem; margin-bottom: 0.5rem; } .results { list-style-type: none; margin: 0; padding: 0; } .results li:not(:last-child) { border-bottom: 1px solid #ddd; } .results li:hover { background-color: #eee; }
HTML
<html> <head> <title>Place Autocomplete Data API Session</title> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> <!-- prettier-ignore --> <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))}) ({key: "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8", v: "weekly"});</script> </head> <body> <gmp-map center="37.7893, -122.4039" zoom="12" map-id="DEMO_MAP_ID"> <div class="controls" slot="control-inline-start-block-start" > <input type="text" class="input" placeholder="Search for a place..." autocomplete="off" /><!-- Turn off the input's own autocomplete (not supported by all browsers).--> <div class="token-status"></div> <div class="title"></div> <ol class="results"></ol> </div> </gmp-map> </body> </html>