สร้างอินเทอร์เฟซการค้นหาด้วยวิดเจ็ตการค้นหา

วิดเจ็ตการค้นหามีอินเทอร์เฟซการค้นหาที่ปรับแต่งได้สำหรับเว็บแอปพลิเคชัน โดยต้องใช้ HTML และ JavaScript น้อยที่สุดในการติดตั้งใช้งาน และรองรับฟีเจอร์ทั่วไป เช่น ข้อมูลประกอบและการแบ่งหน้า นอกจากนี้ คุณยังปรับแต่งอินเทอร์เฟซด้วย CSS และ JavaScript ได้ด้วย

หากต้องการความยืดหยุ่นมากขึ้น ให้ใช้ Query API ดู การสร้างอินเทอร์เฟซการค้นหาด้วย Query API

สร้างอินเทอร์เฟซการค้นหา

การสร้างอินเทอร์เฟซการค้นหาต้องทำตามขั้นตอนต่อไปนี้

  1. กำหนดค่าแอปพลิเคชันการค้นหา
  2. สร้างรหัสไคลเอ็นต์สำหรับแอปพลิเคชัน
  3. เพิ่มมาร์กอัป HTML สำหรับช่องค้นหาและผลการค้นหา
  4. โหลดวิดเจ็ตในหน้าเว็บ
  5. เริ่มต้นวิดเจ็ต

กำหนดค่าแอปพลิเคชันการค้นหา

อินเทอร์เฟซการค้นหาแต่ละรายการต้องมี แอปพลิเคชันการค้นหา ที่กำหนดไว้ในคอนโซลผู้ดูแลระบบ แอปพลิเคชันมีการตั้งค่าการค้นหา เช่น แหล่งข้อมูล ข้อมูลประกอบ และพารามิเตอร์คุณภาพการค้นหา

หากต้องการสร้างแอปพลิเคชันการค้นหา โปรดดู สร้างประสบการณ์การค้นหาที่กำหนดเอง

สร้างรหัสไคลเอ็นต์สำหรับแอปพลิเคชัน

นอกเหนือจากขั้นตอนใน กำหนดค่าการเข้าถึง Cloud Search APIแล้ว ให้สร้างรหัสไคลเอ็นต์สำหรับเว็บแอปพลิเคชัน

กำหนดค่าโปรเจ็กต์

เมื่อกำหนดค่าโปรเจ็กต์ ให้ทำดังนี้

  • เลือกประเภทไคลเอ็นต์เป็นเว็บเบราว์เซอร์
  • ระบุ URI ต้นทาง ของ แอป
  • จดรหัสไคลเอ็นต์ วิดเจ็ตไม่จำเป็นต้องใช้รหัสลับไคลเอ็นต์

ดูข้อมูลเพิ่มเติมได้ที่ OAuth 2.0 สำหรับเว็บ แอปพลิเคชันฝั่งไคลเอ็นต์

เพิ่มมาร์กอัป HTML

วิดเจ็ตต้องมีองค์ประกอบ HTML ต่อไปนี้

  • องค์ประกอบ input สำหรับช่องค้นหา
  • องค์ประกอบสำหรับยึดกล่องโต้ตอบคำแนะนำ
  • องค์ประกอบสำหรับผลการค้นหา
  • (ไม่บังคับ) องค์ประกอบสำหรับการควบคุมข้อมูลประกอบ

ข้อมูลโค้ดนี้แสดงองค์ประกอบที่ระบุด้วยแอตทริบิวต์ id

serving/widget/public/with_css/index.html
<div id="search_bar">
  <div id="suggestions_anchor">
    <input type="text" id="search_input" placeholder="Search for...">
  </div>
</div>
<div id="facet_results"></div>
<div id="search_results"></div>

โหลดวิดเจ็ต

รวมตัวโหลดโดยใช้แท็ก <script>

serving/widget/public/with_css/index.html
<!-- Google API loader -->
<script src="https://apis.google.com/js/api.js?mods=enable_cloud_search_widget&onload=onLoad" async defer></script>

ระบุการเรียกกลับ onload เมื่อตัวโหลดพร้อมแล้ว ให้เรียก gapi.load() เพื่อโหลดไคลเอ็นต์ API, การลงชื่อเข้าใช้ด้วย Google และ Cloud Search โมดูล

serving/widget/public/with_css/app.js
/**
* Load the cloud search widget & auth libraries. Runs after
* the initial gapi bootstrap library is ready.
*/
function onLoad() {
  gapi.load('client:auth2:cloudsearch-widget', initializeApp)
}

เริ่มต้นวิดเจ็ต

เริ่มต้นไลบรารีของไคลเอ็นต์โดยใช้ gapi.client.init() หรือ gapi.auth2.init() ด้วยรหัสไคลเอ็นต์และขอบเขต https://www.googleapis.com/auth/cloud_search.query ใช้คลาส Builder เพื่อกำหนดค่าและผูกวิดเจ็ต

ตัวอย่างการเริ่มต้น

serving/widget/public/with_css/app.js
/**
 * Initialize the app after loading the Google API client &
 * Cloud Search widget.
 */
function initializeApp() {
  // Load client ID & search app.
  loadConfiguration().then(function() {
    // Set API version to v1.
    gapi.config.update('cloudsearch.config/apiVersion', 'v1');

    // Build the result container and bind to DOM elements.
    var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
      .setSearchApplicationId(searchApplicationName)
      .setSearchResultsContainerElement(document.getElementById('search_results'))
      .setFacetResultsContainerElement(document.getElementById('facet_results'))
      .build();

    // Build the search box and bind to DOM elements.
    var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
      .setSearchApplicationId(searchApplicationName)
      .setInput(document.getElementById('search_input'))
      .setAnchor(document.getElementById('suggestions_anchor'))
      .setResultsContainer(resultsContainer)
      .build();
  }).then(function() {
    // Init API/oauth client w/client ID.
    return gapi.auth2.init({
        'clientId': clientId,
        'scope': 'https://www.googleapis.com/auth/cloud_search.query'
    });
  });
}

ตัวแปรการกำหนดค่า

serving/widget/public/with_css/app.js
/**
* Client ID from OAuth credentials.
*/
var clientId = "...apps.googleusercontent.com";

/**
* Full resource name of the search application, such as
* "searchapplications/<your-id>".
*/
var searchApplicationName = "searchapplications/...";

ปรับแต่งประสบการณ์การลงชื่อเข้าใช้

วิดเจ็ตจะแจ้งให้ผู้ใช้ลงชื่อเข้าใช้เมื่อเริ่มพิมพ์ คุณสามารถใช้ การลงชื่อเข้าใช้ด้วย Google สำหรับเว็บไซต์เพื่อมอบประสบการณ์การใช้งานที่ปรับแต่งได้

ให้สิทธิ์ผู้ใช้โดยตรง

ใช้ฟีเจอร์ลงชื่อเข้าใช้ด้วย Google เพื่อตรวจสอบและจัดการสถานะการลงชื่อเข้าใช้ ตัวอย่างนี้ใช้ GoogleAuth.signIn() เมื่อมีการคลิกปุ่ม

serving/widget/public/with_signin/app.js
// Handle sign-in/sign-out.
let auth = gapi.auth2.getAuthInstance();

// Watch for sign in status changes to update the UI appropriately.
let onSignInChanged = (isSignedIn) => {
  // Update UI to switch between signed in/out states
  // ...
}
auth.isSignedIn.listen(onSignInChanged);
onSignInChanged(auth.isSignedIn.get()); // Trigger with current status.

// Connect sign-in/sign-out buttons.
document.getElementById("sign-in").onclick = function(e) {
  auth.signIn();
};
document.getElementById("sign-out").onclick = function(e) {
  auth.signOut();
};

ลงชื่อเข้าใช้ผู้ใช้โดยอัตโนมัติ

ให้สิทธิ์แอปพลิเคชันล่วงหน้าสำหรับผู้ใช้ในองค์กรเพื่อลดความซับซ้อนในการลงชื่อเข้าใช้ ซึ่งจะเป็นประโยชน์เมื่อใช้ Cloud Identity Aware Proxy ด้วย ดู ใช้ Google Sign-In กับแอปไอที แอป

ปรับแต่งอินเทอร์เฟซ

คุณสามารถเปลี่ยนลักษณะของวิดเจ็ตได้โดยทำดังนี้

  • ลบล้างรูปแบบด้วย CSS
  • ตกแต่งองค์ประกอบด้วยอะแดปเตอร์
  • สร้างองค์ประกอบที่กำหนดเองด้วยอะแดปเตอร์

ลบล้างรูปแบบด้วย CSS

วิดเจ็ตมี CSS ของตัวเอง หากต้องการลบล้าง ให้ใช้ตัวเลือกบรรพบุรุษเพื่อเพิ่มความเฉพาะเจาะจง

#suggestions_anchor .cloudsearch_suggestion_container {
  font-size: 14px;
}

ดูข้อมูลอ้างอิง คลาสCSS ที่รองรับ

ตกแต่งองค์ประกอบด้วยอะแดปเตอร์

สร้างและลงทะเบียนอะแดปเตอร์เพื่อแก้ไของค์ประกอบก่อนแสดงผล ตัวอย่างนี้เพิ่มคลาส CSS ที่กำหนดเอง

/**
 * Search box adapter that decorates suggestion elements by
 * adding a custom CSS class.
 */
function SearchBoxAdapter() {}
SearchBoxAdapter.prototype.decorateSuggestionElement = function(element) {
  element.classList.add('my-suggestion');
}

/**
 * Results container adapter that decorates suggestion elements by
 * adding a custom CSS class.
 */
function ResultsContainerAdapter() {}
ResultsContainerAdapter.prototype.decorateSearchResultElement = function(element) {
  element.classList.add('my-result');
}

ลงทะเบียนอะแดปเตอร์ระหว่างการเริ่มต้น

serving/widget/public/with_decorated_element/app.js
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(new ResultsContainerAdapter())
  // ...
  .build();

// Build the search box and bind to DOM elements.
var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
  .setAdapter(new SearchBoxAdapter())
  // ...
  .build();

สร้างองค์ประกอบที่กำหนดเองด้วยอะแดปเตอร์

ใช้ createSuggestionElement, createFacetResultElement หรือ createSearchResultElement เพื่อสร้างคอมโพเนนต์ UI ที่กำหนดเอง ตัวอย่างนี้ใช้ แท็ก HTML <template>

serving/widget/public/with_custom_element/app.js
/**
 * Search box adapter that overrides creation of suggestion elements.
 */
function SearchBoxAdapter() {}
SearchBoxAdapter.prototype.createSuggestionElement = function(suggestion) {
  let template = document.querySelector('#suggestion_template');
  let fragment = document.importNode(template.content, true);
  fragment.querySelector('.suggested_query').textContent = suggestion.suggestedQuery;
  return fragment.firstElementChild;
}

/**
 * Results container adapter that overrides creation of result elements.
 */
function ResultsContainerAdapter() {}
ResultsContainerAdapter.prototype.createSearchResultElement = function(result) {
  let template = document.querySelector('#result_template');
  let fragment = document.importNode(template.content, true);
  fragment.querySelector('.title').textContent = result.title;
  fragment.querySelector('.title').href = result.url;
  let snippetText = result.snippet != null ?
    result.snippet.snippet : '';
  fragment.querySelector('.query_snippet').innerHTML = snippetText;
  return fragment.firstElementChild;
}

ลงทะเบียนอะแดปเตอร์

serving/widget/public/with_custom_element/app.js
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(new ResultsContainerAdapter())
  // ...
  .build();

// Build the search box and bind to DOM elements.
var searchBox = new gapi.cloudsearch.widget.searchbox.Builder()
  .setAdapter(new SearchBoxAdapter())
  // ...
  .build();

องค์ประกอบข้อมูลประกอบที่กำหนดเองต้องเป็นไปตามกฎต่อไปนี้

  • แนบ cloudsearch_facet_bucket_clickable กับองค์ประกอบที่คลิกได้
  • รวมแต่ละบัคเก็ตไว้ใน cloudsearch_facet_bucket_container
  • รักษลําดับบัคเก็ตจากคำตอบ

ตัวอย่างเช่น ข้อมูลโค้ดต่อไปนี้แสดงผลข้อมูลประกอบโดยใช้ลิงก์แทนช่องทำเครื่องหมาย

serving/widget/public/with_custom_facet/app.js
/**
 * Results container adapter that intercepts requests to dynamically
 * change which sources are enabled based on user selection.
 */
function ResultsContainerAdapter() {
  this.selectedSource = null;
}

ResultsContainerAdapter.prototype.createFacetResultElement = function(result) {
  // container for the facet
  var container = document.createElement('div');

  // Add a label describing the facet (operator/property)
  var label = document.createElement('div')
  label.classList.add('facet_label');
  label.textContent = result.operatorName;
  container.appendChild(label);

  // Add each bucket
  for(var i in result.buckets) {
    var bucket = document.createElement('div');
    bucket.classList.add('cloudsearch_facet_bucket_container');

    // Extract & render value from structured value
    // Note: implementation of renderValue() not shown
    var bucketValue = this.renderValue(result.buckets[i].value)
    var link = document.createElement('a');
    link.classList.add('cloudsearch_facet_bucket_clickable');
    link.textContent = bucketValue;
    bucket.appendChild(link);
    container.appendChild(bucket);
  }
  return container;
}

// Renders a value for user display
ResultsContainerAdapter.prototype.renderValue = function(value) {
  // ...
}

ปรับแต่งพฤติกรรมการค้นหา

ลบล้างการตั้งค่าแอปพลิเคชันการค้นหาโดยการสกัดกั้นคำขอด้วยอะแดปเตอร์ ใช้ interceptSearchRequest เพื่อแก้ไขคำขอก่อนดำเนินการ ตัวอย่างนี้จำกัดการค้นหาไว้ที่แหล่งที่มาที่เลือก

serving/widget/public/with_request_interceptor/app.js
/**
 * Results container adapter that intercepts requests to dynamically
 * change which sources are enabled based on user selection.
 */
function ResultsContainerAdapter() {
  this.selectedSource = null;
}
ResultsContainerAdapter.prototype.interceptSearchRequest = function(request) {
  if (!this.selectedSource || this.selectedSource == 'ALL') {
    // Everything selected, fall back to sources defined in the search
    // application.
    request.dataSourceRestrictions = null;
  } else {
    // Restrict to a single selected source.
    request.dataSourceRestrictions = [
      {
        source: {
          predefinedSource: this.selectedSource
        }
      }
    ];
  }
  return request;
}

ลงทะเบียนอะแดปเตอร์

serving/widget/public/with_request_interceptor/app.js
var resultsContainerAdapter = new ResultsContainerAdapter();
// Build the result container and bind to DOM elements.
var resultsContainer = new gapi.cloudsearch.widget.resultscontainer.Builder()
  .setAdapter(resultsContainerAdapter)
  // ...
  .build();

HTML ต่อไปนี้ใช้เพื่อแสดงกล่องเลือกสำหรับการกรองตามแหล่งที่มา

serving/widget/public/with_request_interceptor/index.html
<div>
  <span>Source</span>
  <select id="sources">
    <option value="ALL">All</option>
    <option value="GOOGLE_GMAIL">Gmail</option>
    <option value="GOOGLE_DRIVE">Drive</option>
    <option value="GOOGLE_SITES">Sites</option>
    <option value="GOOGLE_GROUPS">Groups</option>
    <option value="GOOGLE_CALENDAR">Calendar</option>
    <option value="GOOGLE_KEEP">Keep</option>
  </select>
</div>

โค้ดต่อไปนี้จะรอการเปลี่ยนแปลง ตั้งค่าการเลือก และดำเนินการค้นหาอีกครั้งหากจำเป็น

serving/widget/public/with_request_interceptor/app.js
// Handle source selection
document.getElementById('sources').onchange = (e) => {
  resultsContainerAdapter.selectedSource = e.target.value;
  let request = resultsContainer.getCurrentRequest();
  if (request.query) {
    // Re-execute if there's a valid query. The source selection
    // will be applied in the interceptor.
    resultsContainer.resetState();
    resultsContainer.executeRequest(request);
  }
}

นอกจากนี้ คุณยังสกัดกั้นคำตอบการค้นหาได้โดยใช้ interceptSearchResponse ในอะแดปเตอร์

ปักหมุดเวอร์ชัน

  • เวอร์ชัน API: ตั้งค่า cloudsearch.config/apiVersion ก่อนเริ่มต้น
  • เวอร์ชันวิดเจ็ต: ใช้ gapi.config.update('cloudsearch.config/clientVersion', 1.1)

ค่าเริ่มต้นจะเป็น 1.0 หากไม่ได้ตั้งค่าไว้

ตัวอย่างเช่น หากต้องการปักหมุดวิดเจ็ตไว้ที่เวอร์ชัน 1.1 ให้ทำดังนี้

serving/widget/public/basic/app.js
gapi.config.update('cloudsearch.config/apiVersion', 'v1');

รักษาความปลอดภัยของอินเทอร์เฟซการค้นหา

ทำตามแนวทางปฏิบัติแนะนำด้านความปลอดภัยสำหรับเว็บแอปพลิเคชัน โดยเฉพาะอย่างยิ่งเพื่อป้องกัน การโจมตีแบบ Clickjacking

เปิดใช้การแก้ไขข้อบกพร่อง

ใช้ interceptSearchRequest เพื่อเปิดใช้การแก้ไขข้อบกพร่อง

if (!request.requestOptions) {
  request.requestOptions = {};
}
request.requestOptions.debugOptions = {enableDebugging: true};
return request;