ווידג'ט להשלמה אוטומטית של מקומות (תצוגה מקדימה)

הווידג'ט של השלמה אוטומטית של מקומות יוצר שדה קלט טקסט, מספק תחזיות של מקומות ברשימת בחירה בממשק המשתמש ומחזיר פרטי מקומות בתגובה לבחירה של המשתמש. אתם יכולים להשתמש בווידג'ט של השלמה אוטומטית של מקומות כדי להטמיע בדף האינטרנט שלכם ממשק משתמש מלא ומכיל בפני עצמו של השלמה אוטומטית.

דרישות מוקדמות

כדי להשתמש בהשלמה האוטומטית של מקומות (גרסת 'תצוגה מקדימה'), צריך להפעיל את Places API בפרויקט ב-Google Cloud ולציין את ערוץ הבטא (v: "beta") ב-bootstrap loader. פרטים נוספים זמינים במאמר תחילת העבודה.

מה חדש

השיפורים בהשלמה האוטומטית למקומות (תצוגה מקדימה):

  • ממשק המשתמש של הווידג'ט של ההשלמה האוטומטית תומך בתמיכה אזורית (כולל שפות RTL) ב-placeholder של קלט הטקסט, בלוגו של רשימת התחזיות ובתחזיות המקומות.
  • נגישות משופרת, כולל תמיכה בקוראי מסך ובאינטראקציה עם מקלדת.
  • הווידג'ט של ההשלמה האוטומטית מחזיר את סוג המקום החדש כדי לפשט את הטיפול באובייקט המוחזר.
  • תמיכה טובה יותר במכשירים ניידים ובמסכים קטנים.
  • ביצועים טובים יותר ומראה גרפי משופר.

הוספת ווידג'ט של השלמה אוטומטית

אפשר להוסיף ווידג'ט של השלמה אוטומטית לדף אינטרנט או למפה של Google. הווידג'ט של ההשלמה האוטומטית יוצר שדה קלט טקסט, מספק תחזיות של מקומות ברשימת בחירה בממשק המשתמש ומחזיר את פרטי המקום בתגובה לקליק של משתמש באמצעות המאזין gmp-placeselect. בקטע הזה מוסבר איך להוסיף ווידג'ט של השלמה אוטומטית לדף אינטרנט או למפה של Google.

הוספת ווידג'ט של השלמה אוטומטית לדף אינטרנט

כדי להוסיף את הווידג'ט של ההשלמה האוטומטית לדף אינטרנט, יוצרים google.maps.places.PlaceAutocompleteElement חדש ומצרפים אותו לדף, כפי שמתואר בדוגמה הבאה:

TypeScript

// Request needed libraries.
//@ts-ignore
await google.maps.importLibrary("places") as google.maps.PlacesLibrary;
// Create the input HTML element, and append it.
//@ts-ignore
const placeAutocomplete = new google.maps.places.PlaceAutocompleteElement();
//@ts-ignore
document.body.appendChild(placeAutocomplete);

JavaScript

// Request needed libraries.
//@ts-ignore
await google.maps.importLibrary("places");

// Create the input HTML element, and append it.
//@ts-ignore
const placeAutocomplete = new google.maps.places.PlaceAutocompleteElement();

//@ts-ignore
document.body.appendChild(placeAutocomplete);

הקוד המלא לדוגמה

הוספת ווידג'ט של השלמה אוטומטית למפה

כדי להוסיף ווידג'ט של השלמה אוטומטית למפה, יוצרים מופע חדש של google.maps.places.PlaceAutocompleteElement, מוסיפים את PlaceAutocompleteElement ל-div ומעבירים אותו למפה כפקד מותאם אישית, כפי שמתואר בדוגמה הבאה:

TypeScript

//@ts-ignore
const placeAutocomplete = new google.maps.places.PlaceAutocompleteElement();
//@ts-ignore
placeAutocomplete.id = 'place-autocomplete-input';

const card = document.getElementById('place-autocomplete-card') as HTMLElement;
//@ts-ignore
card.appendChild(placeAutocomplete);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(card);

JavaScript

//@ts-ignore
const placeAutocomplete = new google.maps.places.PlaceAutocompleteElement();

//@ts-ignore
placeAutocomplete.id = "place-autocomplete-input";

const card = document.getElementById("place-autocomplete-card");

//@ts-ignore
card.appendChild(placeAutocomplete);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(card);

הקוד המלא לדוגמה

הגבלת החיזויים להשלמה האוטומטית

כברירת מחדל, בתכונה'השלמה אוטומטית של מקומות' מוצגים כל סוגי המקומות, עם נטייה לחיזויים ליד המיקום של המשתמש, והמערכת מאחזרת את כל שדות הנתונים הזמינים של המקום שבחר המשתמש. מגדירים את PlaceAutocompleteElementOptions כדי להציג תחזיות רלוונטיות יותר, על ידי הגבלת התוצאות או הטיה שלהן.

הגבלת התוצאות גורמת לווידג'ט ההשלמה האוטומטית להתעלם מתוצאות שמחוץ לאזור ההגבלה. שיטה נפוצה היא להגביל את התוצאות לגבולות המפה. הטיה של התוצאות גורמת לווידג'ט ההשלמה האוטומטית להציג תוצאות בתוך האזור שצוין, אבל יכול להיות שחלק מההתאמות יהיו מחוץ לאזור הזה.

אם לא תספקו גבולות או חלון תצוגה של מפה, ה-API ינסה לזהות את המיקום של המשתמש מכתובת ה-IP שלו, ויטה את התוצאות למיקום הזה. מומלץ להגדיר גבולות כשהדבר אפשרי. אחרת, משתמשים שונים עשויים לקבל תחזיות שונות. בנוסף, כדי לשפר את התחזיות באופן כללי, חשוב לספק שדה ראייה הגיוני, כמו שדה ראייה שהגדרתם על ידי החלקה או שינוי מרחק התצוגה במפה, או שדה ראייה שהמפתחים הגדירו על סמך המיקום והרדיוס של המכשיר. כשהאפשרות להגדרת רדיוס לא זמינה, 5 ק"מ נחשבים לברירת מחדל הגיונית להשלמה האוטומטית של מקומות. אל תגדירו שדה ראייה עם רדיוס אפס (נקודה אחת), שדה ראייה שרוחב שלו רק כמה מטרים (פחות מ-100 מ') או שדה ראייה שרוחב שלו מכסה את כדור הארץ.

הגבלת החיפוש של מקומות לפי מדינה

כדי להגביל את החיפוש של מקומות למדינה אחת או יותר, צריך להשתמש במאפיין componentRestrictions כדי לציין את קודי המדינות, כפי שמתואר בקטע הקוד הבא:

const pac = new google.maps.places.PlaceAutocompleteElement({
  componentRestrictions: {country: ['us', 'au']},
});

הגבלת חיפוש המקומות לגבולות המפה

כדי להגביל את חיפוש המקומות לגבולות של מפה, משתמשים במאפיין locationRestrictions כדי להוסיף את הגבולות, כפי שמתואר בקטע הקוד הבא:

const pac = new google.maps.places.PlaceAutocompleteElement({
  locationRestriction: map.getBounds(),
});

כשמגבילים את החיפוש לגבולות המפה, חשוב להוסיף מאזין כדי לעדכן את הגבולות כשהם משתנים:

map.addListener('bounds_changed', () => {
  autocomplete.locationRestriction = map.getBounds();
});

כדי להסיר את locationRestriction, מגדירים אותו כ-null.

הטיה בתוצאות החיפוש של מקומות

כדי להטות את תוצאות החיפוש לאזור עגול, משתמשים בנכס locationBias ומעבירים רדיוס, כפי שמתואר כאן:

const autocomplete = new google.maps.places.PlaceAutocompleteElement({
  locationBias: {radius: 100, center: {lat: 50.064192, lng: -130.605469}},
});

כדי להסיר את locationBias, מגדירים אותו כ-null.

הגבלת תוצאות החיפוש של מקומות לסוגים מסוימים

כדי להגביל את תוצאות החיפוש של מקומות לסוגים מסוימים של מקומות, משתמשים במאפיין types ומציינים סוג אחד או יותר, כפי שמתואר כאן:

const autocomplete = new google.maps.places.PlaceAutocompleteElement({
  types: ['establishment'],
});

רשימה מלאה של הסוגים הנתמכים מופיעה בקטע טבלה 3: סוגי הבקשות הנתמכות להשלמה אוטומטית של מקומות.

אחזור פרטי מקום

כדי לקבל את פרטי המקום שנבחר, מוסיפים למעקב gmp-placeselect ל-PlaceAutocompleteElement, כפי שמתואר בדוגמה הבאה:

TypeScript

// Add the gmp-placeselect listener, and display the results.
//@ts-ignore
placeAutocomplete.addEventListener('gmp-placeselect', async ({ place }) => {
    await place.fetchFields({ fields: ['displayName', 'formattedAddress', 'location'] });

    selectedPlaceTitle.textContent = 'Selected Place:';
    selectedPlaceInfo.textContent = JSON.stringify(
        place.toJSON(), /* replacer */ null, /* space */ 2);
});

JavaScript

// Add the gmp-placeselect listener, and display the results.
//@ts-ignore
placeAutocomplete.addEventListener("gmp-placeselect", async ({ place }) => {
  await place.fetchFields({
    fields: ["displayName", "formattedAddress", "location"],
  });
  selectedPlaceTitle.textContent = "Selected Place:";
  selectedPlaceInfo.textContent = JSON.stringify(
    place.toJSON(),
    /* replacer */ null,
    /* space */ 2,
  );
});

הקוד המלא לדוגמה

בדוגמה הקודמת, מאזין האירועים מחזיר אובייקט מסוג Place class. צריך לבצע קריאה ל-place.fetchFields() כדי לקבל את שדות הנתונים של פרטי המקום שנדרשים לאפליקציה.

בקוד לדוגמה הבא, המאזין מבקש מידע על מקום ומציג אותו במפה.

TypeScript

// Add the gmp-placeselect listener, and display the results on the map.
//@ts-ignore
placeAutocomplete.addEventListener('gmp-placeselect', async ({ place }) => {
    await place.fetchFields({ fields: ['displayName', 'formattedAddress', 'location'] });

    // If the place has a geometry, then present it on a map.
    if (place.viewport) {
        map.fitBounds(place.viewport);
    } else {
        map.setCenter(place.location);
        map.setZoom(17);
    }

    let content = '<div id="infowindow-content">' +
    '<span id="place-displayname" class="title">' + place.displayName + '</span><br />' +
    '<span id="place-address">' + place.formattedAddress + '</span>' +
    '</div>';

    updateInfoWindow(content, place.location);
    marker.position = place.location;
});

JavaScript

// Add the gmp-placeselect listener, and display the results on the map.
//@ts-ignore
placeAutocomplete.addEventListener("gmp-placeselect", async ({ place }) => {
  await place.fetchFields({
    fields: ["displayName", "formattedAddress", "location"],
  });
  // If the place has a geometry, then present it on a map.
  if (place.viewport) {
    map.fitBounds(place.viewport);
  } else {
    map.setCenter(place.location);
    map.setZoom(17);
  }

  let content =
    '<div id="infowindow-content">' +
    '<span id="place-displayname" class="title">' +
    place.displayName +
    "</span><br />" +
    '<span id="place-address">' +
    place.formattedAddress +
    "</span>" +
    "</div>";

  updateInfoWindow(content, place.location);
  marker.position = place.location;
});

הקוד המלא לדוגמה

מפות לדוגמה

הקטע הזה מכיל את הקוד המלא של מפות הדוגמה שמוצגות בדף הזה.

רכיב השלמה אוטומטית

בדוגמה הזו מתווסף ווידג'ט של השלמה אוטומטית לדף אינטרנט, והתוצאות מוצגות לכל מקום שנבחר.

TypeScript

async function initMap(): Promise<void> {
    // Request needed libraries.
    //@ts-ignore
    await google.maps.importLibrary("places") as google.maps.PlacesLibrary;
    // Create the input HTML element, and append it.
    //@ts-ignore
    const placeAutocomplete = new google.maps.places.PlaceAutocompleteElement();
    //@ts-ignore
    document.body.appendChild(placeAutocomplete);

    // Inject HTML UI.
    const selectedPlaceTitle = document.createElement('p');
    selectedPlaceTitle.textContent = '';
    document.body.appendChild(selectedPlaceTitle);

    const selectedPlaceInfo = document.createElement('pre');
    selectedPlaceInfo.textContent = '';
    document.body.appendChild(selectedPlaceInfo);

    // Add the gmp-placeselect listener, and display the results.
    //@ts-ignore
    placeAutocomplete.addEventListener('gmp-placeselect', async ({ place }) => {
        await place.fetchFields({ fields: ['displayName', 'formattedAddress', 'location'] });

        selectedPlaceTitle.textContent = 'Selected Place:';
        selectedPlaceInfo.textContent = JSON.stringify(
            place.toJSON(), /* replacer */ null, /* space */ 2);
    });
}

initMap();

JavaScript

async function initMap() {
  // Request needed libraries.
  //@ts-ignore
  await google.maps.importLibrary("places");

  // Create the input HTML element, and append it.
  //@ts-ignore
  const placeAutocomplete = new google.maps.places.PlaceAutocompleteElement();

  //@ts-ignore
  document.body.appendChild(placeAutocomplete);

  // Inject HTML UI.
  const selectedPlaceTitle = document.createElement("p");

  selectedPlaceTitle.textContent = "";
  document.body.appendChild(selectedPlaceTitle);

  const selectedPlaceInfo = document.createElement("pre");

  selectedPlaceInfo.textContent = "";
  document.body.appendChild(selectedPlaceInfo);
  // Add the gmp-placeselect listener, and display the results.
  //@ts-ignore
  placeAutocomplete.addEventListener("gmp-placeselect", async ({ place }) => {
    await place.fetchFields({
      fields: ["displayName", "formattedAddress", "location"],
    });
    selectedPlaceTitle.textContent = "Selected Place:";
    selectedPlaceInfo.textContent = JSON.stringify(
      place.toJSON(),
      /* replacer */ null,
      /* space */ 2,
    );
  });
}

initMap();

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;
}

p {
  font-family: Roboto, sans-serif;
  font-weight: bold;
}

HTML

<html>
  <head>
    <title>Place Autocomplete element</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <p style="font-family: roboto, sans-serif">Search for a place here:</p>

    <!-- 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: "beta"});</script>
  </body>
</html>

ניסיון של דוגמה

השלמה אוטומטית של מפה

בדוגמה הזו מוסבר איך להוסיף ווידג'ט של השלמה אוטומטית למפה של Google.

TypeScript

let map: google.maps.Map;
let marker: google.maps.marker.AdvancedMarkerElement;
let infoWindow: google.maps.InfoWindow;
async function initMap(): Promise<void> {
    // Request needed libraries.
    //@ts-ignore
    const [{ Map }, { AdvancedMarkerElement }] = await Promise.all([
        google.maps.importLibrary("marker"),
        google.maps.importLibrary("places")
      ]);

    // Initialize the map.
    map = new google.maps.Map(document.getElementById('map') as HTMLElement, {
        center: { lat: 40.749933, lng: -73.98633 },
        zoom: 13,
        mapId: '4504f8b37365c3d0',
        mapTypeControl: false,
    });
    //@ts-ignore
    const placeAutocomplete = new google.maps.places.PlaceAutocompleteElement();
    //@ts-ignore
    placeAutocomplete.id = 'place-autocomplete-input';

    const card = document.getElementById('place-autocomplete-card') as HTMLElement;
    //@ts-ignore
    card.appendChild(placeAutocomplete);
    map.controls[google.maps.ControlPosition.TOP_LEFT].push(card);

    // Create the marker and infowindow
    marker = new google.maps.marker.AdvancedMarkerElement({
        map,
    });

    infoWindow = new google.maps.InfoWindow({});

    // Add the gmp-placeselect listener, and display the results on the map.
    //@ts-ignore
    placeAutocomplete.addEventListener('gmp-placeselect', async ({ place }) => {
        await place.fetchFields({ fields: ['displayName', 'formattedAddress', 'location'] });

        // If the place has a geometry, then present it on a map.
        if (place.viewport) {
            map.fitBounds(place.viewport);
        } else {
            map.setCenter(place.location);
            map.setZoom(17);
        }

        let content = '<div id="infowindow-content">' +
        '<span id="place-displayname" class="title">' + place.displayName + '</span><br />' +
        '<span id="place-address">' + place.formattedAddress + '</span>' +
        '</div>';

        updateInfoWindow(content, place.location);
        marker.position = place.location;
    });
}

// Helper function to create an info window.
function updateInfoWindow(content, center) {
    infoWindow.setContent(content);
    infoWindow.setPosition(center);
    infoWindow.open({
        map,
        anchor: marker,
        shouldFocus: false,
    });
}

initMap();

JavaScript

let map;
let marker;
let infoWindow;

async function initMap() {
  // Request needed libraries.
  //@ts-ignore
  const [{ Map }, { AdvancedMarkerElement }] = await Promise.all([
    google.maps.importLibrary("marker"),
    google.maps.importLibrary("places"),
  ]);

  // Initialize the map.
  map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: 40.749933, lng: -73.98633 },
    zoom: 13,
    mapId: "4504f8b37365c3d0",
    mapTypeControl: false,
  });

  //@ts-ignore
  const placeAutocomplete = new google.maps.places.PlaceAutocompleteElement();

  //@ts-ignore
  placeAutocomplete.id = "place-autocomplete-input";

  const card = document.getElementById("place-autocomplete-card");

  //@ts-ignore
  card.appendChild(placeAutocomplete);
  map.controls[google.maps.ControlPosition.TOP_LEFT].push(card);
  // Create the marker and infowindow
  marker = new google.maps.marker.AdvancedMarkerElement({
    map,
  });
  infoWindow = new google.maps.InfoWindow({});
  // Add the gmp-placeselect listener, and display the results on the map.
  //@ts-ignore
  placeAutocomplete.addEventListener("gmp-placeselect", async ({ place }) => {
    await place.fetchFields({
      fields: ["displayName", "formattedAddress", "location"],
    });
    // If the place has a geometry, then present it on a map.
    if (place.viewport) {
      map.fitBounds(place.viewport);
    } else {
      map.setCenter(place.location);
      map.setZoom(17);
    }

    let content =
      '<div id="infowindow-content">' +
      '<span id="place-displayname" class="title">' +
      place.displayName +
      "</span><br />" +
      '<span id="place-address">' +
      place.formattedAddress +
      "</span>" +
      "</div>";

    updateInfoWindow(content, place.location);
    marker.position = place.location;
  });
}

// Helper function to create an info window.
function updateInfoWindow(content, center) {
  infoWindow.setContent(content);
  infoWindow.setPosition(center);
  infoWindow.open({
    map,
    anchor: marker,
    shouldFocus: false,
  });
}

initMap();

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-autocomplete-card {
  background-color: #fff;
  border-radius: 5px;
  box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
  margin: 10px;
  padding: 5px;
  font-family: Roboto, sans-serif;
  font-size: large;
  font-weight: bold;
}

gmp-place-autocomplete {
  width: 300px;
}

#infowindow-content .title {
  font-weight: bold;
}

#map #infowindow-content {
  display: inline;
}

HTML

<html>
  <head>
    <title>Place Autocomplete map</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div class="place-autocomplete-card" id="place-autocomplete-card">
      <p>Search for a place here:</p>
    </div>
    <div id="map"></div>

    <!-- 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: "beta"});</script>
  </body>
</html>

ניסיון של דוגמה

שימוש ברכיב 'בחירת מקום'

הרכיב של בורר המקומות הוא קלט טקסט שמאפשר למשתמשי קצה לחפש כתובת או מקום ספציפיים באמצעות השלמה אוטומטית. הוא חלק מספריית הרכיבים המורחבת, קבוצה של רכיבי אינטרנט שעוזרים למפתחים ליצור מפות ותכונות מיקום טובות יותר מהר יותר.

אתם יכולים להשתמש בכלי להגדרת בורר המקומות כדי ליצור קוד שניתן להטמעה לרכיב בורר מקומות מותאם אישית, ולאחר מכן לייצא אותו לשימוש עם מסגרות פופולריות כמו React ו-Angular, או בלי מסגרת בכלל.