Phần tử danh sách địa điểm

PlaceListElement là một phần tử HTML hiển thị kết quả tìm kiếm địa điểm trong một danh sách. Có hai cách để định cấu hình phần tử gmp-place-list:

Tìm kiếm theo yêu cầu tìm kiếm bằng văn bản

Ví dụ sau đây hiển thị phần tử Danh sách địa điểm để phản hồi nội dung tìm kiếm bằng văn bản của người dùng. Khi bạn chọn một kết quả, một cửa sổ thông tin hiển thị thông tin chi tiết về địa điểm đã chọn sẽ xuất hiện. Để thêm phần tử Danh sách địa điểm vào bản đồ, hãy thêm phần tử gmp-place-list vào trang HTML, như trong đoạn mã sau:

<gmp-map center="-37.813,144.963" zoom="2" map-id="DEMO_MAP_ID">
  <div class="overlay" slot="control-inline-start-block-start">
    <div class="controls">
      <input type="text" class="query-input" />
      <button class="search-button">Search</button>
    </div>
    <div class="list-container">
      <gmp-place-list selectable></gmp-place-list>
    </div>
  </div>
  <gmp-place-details size="large"></gmp-place-details>
</gmp-map>

Một số lệnh gọi querySelector được dùng để chọn các phần tử trang để tương tác:

const map = document.querySelector('gmp-map');
const placeList = document.querySelector('gmp-place-list');
const queryInput = document.querySelector('.query-input');
const searchButton = document.querySelector('.search-button');
const placeDetails = document.querySelector('gmp-place-details');

Khi người dùng nhấp vào nút tìm kiếm, configureFromSearchByTextRequest sẽ được gọi và phần tử Danh sách địa điểm sẽ hiển thị kết quả (các điểm đánh dấu được thêm vào hàm trợ giúp addMarkers). Đoạn mã sau đây cho thấy mã để xử lý các sự kiện nhấp chuột:

searchButton.addEventListener('click', () => {
  for (marker in markers) {
    markers[marker].map = null;
  }
  markers = {};
  if (queryInput.value) {
    placeList
        .configureFromSearchByTextRequest({
          textQuery: queryInput.value,
          locationBias: map.innerMap.getBounds()
        })
        .then(addMarkers);
  }
});

Xem ví dụ về mã hoàn chỉnh

Ví dụ sau đây sử dụng thành phần giao diện người dùng Danh sách địa điểm để hiển thị các địa điểm dựa trên nội dung tìm kiếm bằng văn bản bằng cách sử dụng configureFromSearchByTextRequest, đồng thời thêm các điểm đánh dấu có thể nhấp vào bản đồ để hiển thị thông tin chi tiết về địa điểm khi được chọn.

JavaScript

const map = document.querySelector('gmp-map');
const placeList = document.querySelector('gmp-place-list');
const queryInput = document.querySelector('.query-input');
const searchButton = document.querySelector('.search-button');
const placeDetails = document.querySelector('gmp-place-details');
let markers = {};
let infowindow = {};
async function init() {
    await google.maps.importLibrary('places');
    const {
        InfoWindow
    } = await google.maps.importLibrary('maps');
    infowindow = new InfoWindow();
    // Call geolocation helper function to center map.
    findCurrentLocation();
    map.innerMap.setOptions({
        mapTypeControl: false,
        clickableIcons: false,
    });
    // Handle click on search button.
    searchButton.addEventListener('click', () => {
        for (marker in markers) {
            markers[marker].map = null;
        }
        markers = {};
        if (queryInput.value) {
            placeList
                .configureFromSearchByTextRequest({
                    textQuery: queryInput.value,
                    locationBias: map.innerMap.getBounds()
                })
                .then(addMarkers);
        }
    });
    // Handle the user's selection on the Place List.
    placeList.addEventListener('gmp-placeselect', ({
        place
    }) => {
        markers[place.id].click();
    });
}
// Helper function to add markers.
async function addMarkers() {
    const {
        AdvancedMarkerElement
    } = await google.maps.importLibrary('marker');
    const {
        LatLngBounds
    } = await google.maps.importLibrary('core');
    const bounds = new LatLngBounds();
    if (placeList.places.length > 0) {
        placeList.places.forEach((place) => {
            let marker = new AdvancedMarkerElement({
                map: map.innerMap,
                position: place.location
            });
            marker.metadata = {
                id: place.id
            };
            markers[place.id] = marker;
            bounds.extend(place.location);
            marker.addListener('click', (event) => {
                if (infowindow.isOpen) {
                    infowindow.close();
                }
                placeDetails.configureFromPlace(place);
                placeDetails.style.width = '350px';
                infowindow.setOptions({
                    content: placeDetails
                });
                infowindow.open({
                    anchor: marker,
                    map: map.innerMap
                });
                placeDetails.addEventListener('gmp-load', () => {
                    map.innerMap.fitBounds(
                        place.viewport, {
                            top: placeDetails.offsetHeight || 206,
                            left: 200
                        });
                });
            });
            map.innerMap.setCenter(bounds.getCenter());
            map.innerMap.fitBounds(bounds);
        });
    }
}
// Helper function to center map on current device location.
async function findCurrentLocation() {
    const {
        LatLng
    } = await google.maps.importLibrary('core');
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(
            (position) => {
                const pos =
                    new LatLng(position.coords.latitude, position.coords.longitude);
                map.innerMap.panTo(pos);
                map.innerMap.setZoom(16);
            },
            () => {
                console.log('The Geolocation service failed.');
                map.innerMap.setZoom(16);
            },
        );
    } else {
        console.log('Your browser doesn\'t support geolocation');
        map.innerMap.setZoom(16);
    }
}

init();

CSS

html,
body {
    height: 100%;
    margin: 0;
}
body {
    display: flex;
    flex-direction: column;
    font-family: Arial, Helvetica, sans-serif;
}
h1 {
    font-size: 16px;
    text-align: center;
}
gmp-map {
    box-sizing: border-box;
    padding: 0 20px 20px;
}
.overlay {
    margin: 20px;
    width: 400px;
}
.controls {
    display: flex;
    gap: 10px;
    margin-bottom: 10px;
    height: 32px;
}
.search-button {
    background-color: #5491f5;
    color: #fff;
    border: 1px solid #ccc;
    border-radius: 5px;
    width: 100px;
}
.query-input {
    border: 1px solid #ccc;
    border-radius: 5px;
    flex-grow: 1;
    padding: 10px;
}
.list-container {
    height: 600px;
    overflow: auto;
    border-radius: 10px;
}
gmp-place-list {
    background-color: #fff;
}
gmp-place-details {
    width: 320px;
}

HTML

<!DOCTYPE html>
<html>
  <head>
    <title>Place List Text Search with Google Maps</title>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="style.css">

  </head>
  <body>
    <h1>Place List Text Search with Google Maps</h1>
    <gmp-map center="-37.813,144.963" zoom="2" map-id="DEMO_MAP_ID">
        <div class="overlay" slot="control-inline-start-block-start">
          <div class="controls">
            <input type="text" class="query-input" />
            <button class="search-button">Search</button>
          </div>
          <div class="list-container">
            <gmp-place-list selectable></gmp-place-list>
          </div>
        </div>
        <gmp-place-details size="large"></gmp-place-details>
    </gmp-map>
    <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: "YOUR_API_KEY",
        v: "alpha"
      });
    </script>
  </body>
</html>

Yêu cầu tìm kiếm lân cận

Ví dụ sau đây hiển thị phần tử Danh sách địa điểm để phản hồi nội dung tìm kiếm ở gần. Để đơn giản, chỉ có 3 loại địa điểm được liệt kê: quán cà phê, nhà hàng và trạm sạc xe điện. Khi bạn chọn một kết quả, một cửa sổ thông tin hiển thị thông tin chi tiết về địa điểm đã chọn sẽ xuất hiện. Để thêm phần tử Danh sách địa điểm vào bản đồ, hãy thêm phần tử gmp-place-list vào trang HTML như trong đoạn mã sau:

<gmp-map center="-37.813,144.963" zoom="10" map-id="DEMO_MAP_ID">
<div class="overlay" slot="control-inline-start-block-start">
  <div class="controls">
    <select name="types" class="type-select">
      <option value="">Select a place type</option>
      <option value="cafe">Cafe</option>
      <option value="restaurant">Restaurant</option>
      <option value="electric_vehicle_charging_station">
        EV charging station
      </option>
    </select>
    <button class="search-button">Search</button>
  </div>
  <div class="list-container">
    <gmp-place-list selectable></gmp-place-list>
  </div>
</div>

<gmp-place-details size="large"></gmp-place-details>
</gmp-map>

Một số lệnh gọi querySelector được dùng để chọn các phần tử trang để tương tác:

const map = document.querySelector("gmp-map");
const placeList = document.querySelector('gmp-place-list');
const typeSelect = document.querySelector('.type-select');
const searchButton = document.querySelector('.search-button');
const placeDetails = document.querySelector('gmp-place-details');

Khi người dùng nhấp vào nút tìm kiếm, configureFromSearchNearbyRequest sẽ được gọi và phần tử Danh sách địa điểm sẽ hiển thị kết quả (các điểm đánh dấu được thêm vào hàm trợ giúp addMarkers). Đoạn mã sau đây cũng cho thấy mã để xử lý các sự kiện nhấp trên Danh sách địa điểm bằng sự kiện gmp-placeselect:

searchButton.addEventListener('click', () => {
  for (marker in markers) {
    markers[marker].map = null;
  }
  markers = {};
  if (typeSelect.value) {
    placeList
        .configureFromSearchNearbyRequest({
          locationRestriction: getContainingCircle(map.innerMap.getBounds()),
          includedPrimaryTypes: [typeSelect.value],
        })
        .then(addMarkers);
    placeList.addEventListener('gmp-placeselect', ({place}) => {
      markers[place.id].click();
    });
  }
});

Xem ví dụ về mã hoàn chỉnh

Ví dụ sau sử dụng thành phần giao diện người dùng Danh sách địa điểm để hiển thị các địa điểm dựa trên nội dung tìm kiếm lân cận bằng cách sử dụng configureFromSearchNearbyRequest, đồng thời thêm các điểm đánh dấu có thể nhấp vào bản đồ để hiển thị thông tin chi tiết về địa điểm khi được chọn.

JavaScript

const map = document.querySelector("gmp-map");
const placeList = document.querySelector("gmp-place-list");
const typeSelect = document.querySelector(".type-select");
const searchButton = document.querySelector(".search-button");
const placeDetails = document.querySelector("gmp-place-details");
let markers = {};
let infowindow = {};
let mapCenter;

async function init() {
    await google.maps.importLibrary("places");
    const {InfoWindow} = await google.maps.importLibrary("maps");
    const { spherical } = await google.maps.importLibrary("geometry");

    infowindow = new InfoWindow();

    function getContainingCircle(bounds) {
        const diameter = spherical.computeDistanceBetween(
            bounds.getNorthEast(),
            bounds.getSouthWest()
        );
        return { center: bounds.getCenter(), radius: diameter / 2 };
    }

    findCurrentLocation();

    map.innerMap.setOptions({
        mapTypeControl: false,
        clickableIcons: false,
    });

    searchButton.addEventListener("click", () => {
        for(marker in markers){
            markers[marker].map = null;
        }
        markers = {};

        if (typeSelect.value) {
            placeList.configureFromSearchNearbyRequest({
                locationRestriction: getContainingCircle(
                    map.innerMap.getBounds()
                ),
                includedPrimaryTypes: [typeSelect.value],
            }).then(addMarkers);

            placeList.addEventListener("gmp-placeselect", ({ place }) => {
                markers[place.id].click();
            });
        }
    });
}

async function addMarkers(){
    const { AdvancedMarkerElement } = await google.maps.importLibrary("marker");
    const { LatLngBounds } = await google.maps.importLibrary("core");

    const bounds = new LatLngBounds();

    if(placeList.places.length > 0){
        placeList.places.forEach((place) => {
            let marker = new AdvancedMarkerElement({
                map: map.innerMap,
                position: place.location
            });

            marker.metadata = {id: place.id};
            markers[place.id] = marker;
            bounds.extend(place.location);
            // pinMarkers.push(marker);

            marker.addListener('click',(event) => {
                if(infowindow.isOpen){
                    infowindow.close();
                }
                placeDetails.configureFromPlace(place);
                placeDetails.style.width = "350px";
                infowindow.setOptions({
                    content: placeDetails
                });
                infowindow.open({
                    anchor: marker,
                    map: map.innerMap
                });
                placeDetails.addEventListener('gmp-load',() => {
                    map.innerMap.fitBounds(place.viewport, {top: placeDetails.offsetHeight || 206, left: 200});
                });

            });
            map.innerMap.setCenter(bounds.getCenter());
            map.innerMap.fitBounds(bounds);
        });
    }
}

async function findCurrentLocation(){
    const { LatLng } = await google.maps.importLibrary("core")
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(
          (position) => {
            const pos = new LatLng(position.coords.latitude,position.coords.longitude);
            map.innerMap.panTo(pos);
            map.innerMap.setZoom(14);
          },
          () => {
            console.log('The Geolocation service failed.');
            map.innerMap.setZoom(14);
          },
        );
      } else {
        console.log("Your browser doesn't support geolocation");
        map.innerMap.setZoom(14);
      }

}

init();
  

CSS

html,
  body {
      height: 100%;
      margin: 0;
  }

  body {
      display: flex;
      flex-direction: column;
      font-family: Arial, Helvetica, sans-serif;
  }

  h1 {
      font-size: 16px;
      text-align: center;
  }

  gmp-map {
      box-sizing: border-box;
      padding: 0 20px 20px;
  }

  .overlay {
      margin: 20px;
      width: 400px;
  }

  .controls {
      display: flex;
      gap: 10px;
      margin-bottom: 10px;
      height: 32px;
  }

  .search-button {
      background-color: #5491f5;
      color: #fff;
      border: 1px solid #ccc;
      border-radius: 5px;
      width: 100px;
  }

  .type-select {
      border: 1px solid #ccc;
      border-radius: 5px;
      flex-grow: 1;
      padding: 0 10px;
  }

  .list-container {
      height: 600px;
      overflow: auto;
      border-radius: 10px;
  }

  gmp-place-list {
      background-color: #fff;
  }

HTML

<!DOCTYPE html>
<html>
  <head>
    <title>Place List Nearby Search with Google Maps</title>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="style.css">
  </head>
  <body>
    <h1>Place List Nearby Search with Google Maps</h1>
    <gmp-map center="-37.813,144.963" zoom="10" map-id="DEMO_MAP_ID">
      <div class="overlay" slot="control-inline-start-block-start">
        <div class="controls">
          <select name="types" class="type-select">
            <option value="">Select a place type</option>
            <option value="cafe">Cafe</option>
            <option value="restaurant">Restaurant</option>
            <option value="electric_vehicle_charging_station">
              EV charging station
            </option>
          </select>
          <button class="search-button">Search</button>
        </div>
        <div class="list-container">
          <gmp-place-list selectable></gmp-place-list>
        </div>
      </div>
      <gmp-place-details size="large"></gmp-place-details>
    </gmp-map>

    <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: "YOUR_API_KEY",
        v: "alpha"
      });
    </script>
  </body>
</html>