API Dữ liệu tự động hoàn thành địa điểm

Nhà phát triển ở Khu vực kinh tế Châu Âu (EEA)

Place Autocomplete Data API cho phép bạn tìm nạp các cụm từ dự đoán về địa điểm theo phương thức lập trình để tạo trải nghiệm tự động hoàn thành tuỳ chỉnh với mức độ kiểm soát chi tiết hơn so với mức độ kiểm soát có thể có với tiện ích tự động hoàn thành. Trong hướng dẫn này, bạn sẽ tìm hiểu cách sử dụng Place Autocomplete Data API để đưa ra các yêu cầu tự động hoàn thành dựa trên cụm từ tìm kiếm của người dùng.

Ví dụ sau đây minh hoạ một chế độ tích hợp đơn giản cho tính năng nhập liệu dự đoán. Nhập cụm từ tìm kiếm, chẳng hạn như "pizza" hoặc "poke", sau đó nhấp để chọn kết quả bạn muốn.

Yêu cầu tự động hoàn thành

Một yêu cầu tự động hoàn thành sẽ lấy chuỗi đầu vào của cụm từ tìm kiếm và trả về danh sách các dự đoán về địa điểm. Để thực hiện yêu cầu tự động hoàn thành, hãy gọi fetchAutocompleteSuggestions() và truyền một yêu cầu có các thuộc tính cần thiết. Thuộc tính input chứa chuỗi cần tìm kiếm; trong một ứng dụng thông thường, giá trị này sẽ được cập nhật khi người dùng nhập một cụm từ tìm kiếm. Yêu cầu phải có sessionToken, được dùng cho mục đích thanh toán.

Đoạn mã sau đây cho biết cách tạo một phần nội dung yêu cầu và thêm mã thông báo phiên, sau đó gọi fetchAutocompleteSuggestions() để nhận danh sách 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;

Hạn chế cụm từ gợi ý của tính năng Tự động hoàn thành

Theo mặc định, tính năng Tự động hoàn thành địa điểm sẽ trình bày tất cả các loại địa điểm, thiên về những dự đoán gần vị trí của người dùng và tìm nạp tất cả các trường dữ liệu có sẵn cho địa điểm mà người dùng đã chọn. Đặt các lựa chọn Tự động hoàn thành tên địa điểm để đưa ra những dự đoán phù hợp hơn bằng cách hạn chế hoặc thiên vị kết quả.

Việc hạn chế kết quả khiến tiện ích Tự động hoàn thành bỏ qua mọi kết quả nằm ngoài khu vực hạn chế. Một phương pháp thường dùng là hạn chế kết quả trong phạm vi ranh giới trên bản đồ. Việc điều chỉnh kết quả giúp tiện ích Tự động hoàn thành hiển thị kết quả trong khu vực được chỉ định, nhưng một số kết quả trùng khớp có thể nằm ngoài khu vực đó.

Dùng thuộc tính origin để chỉ định điểm xuất phát mà từ đó tính khoảng cách trắc địa đến đích đến. Nếu bạn bỏ qua giá trị này, khoảng cách sẽ không được trả về.

Sử dụng thuộc tính includedPrimaryTypes để chỉ định tối đa 5 loại địa điểm. Nếu bạn không chỉ định loại nào, thì hệ thống sẽ trả về địa điểm thuộc tất cả các loại.

Xem tài liệu tham khảo API

Nhận thông tin chi tiết về địa điểm

Để trả về một đối tượng Place từ kết quả dự đoán địa điểm, trước tiên, hãy gọi toPlace(), sau đó gọi fetchFields() trên đối tượng Place thu được (mã phiên từ kết quả dự đoán địa điểm sẽ tự động được đưa vào). Khi bạn gọi fetchFields(), phiên tự động hoàn thành sẽ kết thúc.

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;

Mã thông báo phiên

Mã thông báo phiên sẽ nhóm các giai đoạn truy vấn và lựa chọn của một cụm từ tìm kiếm tự động hoàn thành của người dùng thành một phiên riêng biệt cho mục đích thanh toán. Phiên bắt đầu khi người dùng bắt đầu nhập. Phiên kết thúc khi người dùng chọn một địa điểm và thực hiện lệnh gọi đến Địa điểm chi tiết.

Để tạo mã thông báo phiên mới và thêm mã thông báo đó vào một yêu cầu, hãy tạo một thực thể của AutocompleteSessionToken, sau đó đặt thuộc tính sessionToken của yêu cầu để sử dụng mã thông báo như trong đoạn mã sau:

// Create a session token.
const token = new AutocompleteSessionToken();

// Add the token to the request.
// @ts-ignore
request.sessionToken = token;

Một phiên sẽ kết thúc khi fetchFields() được gọi. Sau khi tạo thực thể Place, bạn không cần truyền mã thông báo phiên đến fetchFields() vì việc này sẽ được xử lý tự động.

await place.fetchFields({
  fields: ["displayName", "formattedAddress"],
});

Tạo mã thông báo phiên cho phiên tiếp theo bằng cách tạo một phiên bản mới của AutocompleteSessionToken.

Đề xuất về mã thông báo phiên:

  • Sử dụng mã thông báo phiên cho tất cả các lệnh gọi Place Autocomplete.
  • Tạo một mã thông báo mới cho mỗi phiên.
  • Truyền một mã thông báo phiên duy nhất cho mỗi phiên mới. Việc sử dụng cùng một mã thông báo cho nhiều phiên sẽ khiến mỗi yêu cầu bị tính phí riêng lẻ.

Bạn có thể tuỳ ý bỏ qua mã thông báo phiên tự động hoàn thành trong một yêu cầu. Nếu bạn bỏ qua mã thông báo phiên, thì mỗi yêu cầu sẽ được tính phí riêng, kích hoạt SKU Tự động hoàn thành – Theo yêu cầu. Nếu bạn dùng lại mã thông báo phiên, thì phiên đó sẽ được coi là không hợp lệ và các yêu cầu sẽ bị tính phí như thể bạn không cung cấp mã thông báo phiên.

Ví dụ:

Khi người dùng nhập một cụm từ tìm kiếm, yêu cầu tự động hoàn thành sẽ được gọi sau mỗi vài lần nhấn phím (không phải cho mỗi ký tự) và một danh sách kết quả có thể có sẽ được trả về. Khi người dùng chọn một kết quả trong danh sách kết quả, lựa chọn đó được tính là một yêu cầu và tất cả các yêu cầu được đưa ra trong quá trình tìm kiếm sẽ được kết hợp và tính là một yêu cầu duy nhất. Nếu người dùng chọn một địa điểm, thì cụm từ tìm kiếm sẽ được cung cấp miễn phí và bạn chỉ phải trả phí cho yêu cầu về dữ liệu Địa điểm. Nếu người dùng không đưa ra lựa chọn trong vòng vài phút kể từ khi bắt đầu phiên, thì chỉ truy vấn tìm kiếm mới bị tính phí.

Theo quan điểm của một ứng dụng, luồng sự kiện diễn ra như sau:

  1. Người dùng bắt đầu nhập một cụm từ tìm kiếm để tìm "Paris, Pháp".
  2. Khi phát hiện thấy dữ liệu đầu vào của người dùng, ứng dụng sẽ tạo một mã thông báo phiên mới, "Mã thông báo A".
  3. Khi người dùng nhập, cứ vài ký tự, API sẽ đưa ra một yêu cầu tự động hoàn thành, hiển thị một danh sách kết quả tiềm năng mới cho từng ký tự:
    "P"
    "Par"
    "Paris"
    "Paris, Fr"
  4. Khi người dùng chọn:
    • Tất cả các yêu cầu phát sinh từ truy vấn đều được nhóm và thêm vào phiên do "Mã thông báo A" đại diện, dưới dạng một yêu cầu duy nhất.
    • Lựa chọn của người dùng được tính là một yêu cầu Chi tiết về địa điểm và được thêm vào phiên do "Mã thông báo A" đại diện.
  5. Phiên kết thúc và ứng dụng loại bỏ "Mã thông báo A".
Tìm hiểu về cách tính phí cho phiên

Mã ví dụ hoàn chỉnh

Phần này chứa các ví dụ hoàn chỉnh minh hoạ cách sử dụng Place Autocomplete Data API .

Cụm từ gợi ý của tính năng tự động hoàn thành địa điểm

Ví dụ sau đây minh hoạ cách gọi fetchAutocompleteSuggestions() cho đầu vào "Tadi", sau đó gọi toPlace() trên kết quả dự đoán đầu tiên, tiếp theo là gọi fetchFields() để lấy thông tin chi tiết về địa điểm.

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>

Dùng thử mẫu

Tính năng tự động hoàn thành địa điểm nhập trước với các phiên

Ví dụ này minh hoạ việc gọi fetchAutocompleteSuggestions() dựa trên các cụm từ tìm kiếm của người dùng, cho thấy danh sách các địa điểm được dự đoán để phản hồi và cuối cùng là truy xuất thông tin chi tiết về địa điểm cho địa điểm đã chọn. Ví dụ này cũng minh hoạ cách sử dụng mã thông báo phiên để nhóm một truy vấn của người dùng với yêu cầu Chi tiết về địa điểm cuối cùng.

TypeScript

let titleElement;
let resultsContainerElement;
let inputElement;

let newestRequestId = 0;

// Add an initial request body.
const request = {
    input: '',
    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',
};

function init() {
    titleElement = document.getElementById('title');
    resultsContainerElement = document.getElementById('results');
    inputElement = document.querySelector('input');
    inputElement.addEventListener('input', makeAutocompleteRequest);
    refreshToken(request);
}

async function makeAutocompleteRequest(inputEvent) {
    // Reset elements and exit if an empty string is received.
    if (inputEvent.target.value == '') {
        titleElement.innerText = '';
        resultsContainerElement.replaceChildren();
        return;
    }

    // Add the latest char sequence to the request.
    request.input = inputEvent.target.value;

    // To avoid race conditions, store the request ID and compare after the request.
    const requestId = ++newestRequestId;

    // Fetch autocomplete suggestions and show them in a list.
    // @ts-ignore
    const { suggestions } = await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(request);

    // If the request has been superseded by a newer request, do not render the output.
    if (requestId !== newestRequestId) return;

    titleElement.innerText = `Query predictions for "${request.input}"`;

    // Clear the list first.
    resultsContainerElement.replaceChildren();

    for (const suggestion of suggestions) {
        const placePrediction = suggestion.placePrediction;

        // Create a link for the place, add an event handler to fetch the place.
        const a = document.createElement('a');
        a.addEventListener('click', () => {
            onPlaceSelected(placePrediction!.toPlace());
        });
        a.innerText = placePrediction!.text.toString();

        // Create a new list item element.
        const li = document.createElement('li');
        li.appendChild(a);
        resultsContainerElement.appendChild(li);
    }
}

// Event handler for clicking on a suggested place.
async function onPlaceSelected(place) {
    await place.fetchFields({
        fields: ['displayName', 'formattedAddress'],
    });
    const placeText = document.createTextNode(`${place.displayName}: ${place.formattedAddress}`);
    resultsContainerElement.replaceChildren(placeText);
    titleElement.innerText = 'Selected Place:';
    inputElement.value = '';
    refreshToken(request);
}

// Helper function to refresh the session token.
function refreshToken(request) {
    // Create a new session token and add it to the request.
    request.sessionToken = new google.maps.places.AutocompleteSessionToken();
}

declare global {
    interface Window {
      init: () => void;
    }
  }
  window.init = init;

JavaScript

let titleElement;
let resultsContainerElement;
let inputElement;
let newestRequestId = 0;
// Add an initial request body.
const request = {
    input: '',
    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',
};
function init() {
    titleElement = document.getElementById('title');
    resultsContainerElement = document.getElementById('results');
    inputElement = document.querySelector('input');
    inputElement.addEventListener('input', makeAutocompleteRequest);
    refreshToken(request);
}
async function makeAutocompleteRequest(inputEvent) {
    // Reset elements and exit if an empty string is received.
    if (inputEvent.target.value == '') {
        titleElement.innerText = '';
        resultsContainerElement.replaceChildren();
        return;
    }
    // Add the latest char sequence to the request.
    request.input = inputEvent.target.value;
    // To avoid race conditions, store the request ID and compare after the request.
    const requestId = ++newestRequestId;
    // Fetch autocomplete suggestions and show them in a list.
    // @ts-ignore
    const { suggestions } = await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(request);
    // If the request has been superseded by a newer request, do not render the output.
    if (requestId !== newestRequestId)
        return;
    titleElement.innerText = `Query predictions for "${request.input}"`;
    // Clear the list first.
    resultsContainerElement.replaceChildren();
    for (const suggestion of suggestions) {
        const placePrediction = suggestion.placePrediction;
        // Create a link for the place, add an event handler to fetch the place.
        const a = document.createElement('a');
        a.addEventListener('click', () => {
            onPlaceSelected(placePrediction.toPlace());
        });
        a.innerText = placePrediction.text.toString();
        // Create a new list item element.
        const li = document.createElement('li');
        li.appendChild(a);
        resultsContainerElement.appendChild(li);
    }
}
// Event handler for clicking on a suggested place.
async function onPlaceSelected(place) {
    await place.fetchFields({
        fields: ['displayName', 'formattedAddress'],
    });
    const placeText = document.createTextNode(`${place.displayName}: ${place.formattedAddress}`);
    resultsContainerElement.replaceChildren(placeText);
    titleElement.innerText = 'Selected Place:';
    inputElement.value = '';
    refreshToken(request);
}
// Helper function to refresh the session token.
function refreshToken(request) {
    // Create a new session token and add it to the request.
    request.sessionToken = new google.maps.places.AutocompleteSessionToken();
}
window.init = 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;
}

a {
  cursor: pointer;
  text-decoration: underline;
  color: blue;
}

input {
  width: 300px;
}

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>
  </head>
  <body>
    <input id="input" type="text" placeholder="Search for a place..." />
    <div id="title"></div>
    <ul id="results"></ul>
    <img
      class="powered-by-google"
      src="https://storage.googleapis.com/geo-devrel-public-buckets/powered_by_google_on_white.png"
      alt="Powered by Google"
    />

    <!-- 
      The `defer` attribute causes the script to execute after the full HTML
      document has been parsed. For non-blocking uses, avoiding race conditions,
      and consistent behavior across browsers, consider loading using Promises. See
      https://developers.google.com/maps/documentation/javascript/load-maps-js-api
      for more information.
      -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8&callback=init&libraries=places&v=weekly"
      defer
    ></script>
  </body>
</html>

Dùng thử mẫu