Suchoberfläche mit dem Such-Widget erstellen

Das Such-Widget bietet eine anpassbare Suchoberfläche für Webanwendungen. Für das Widget wird nur wenig HTML- und JavaScript-Code benötigt, um gängige Suchfunktionen wie Attribute und Paginierung zu implementieren und zu aktivieren. Mit CSS und JavaScript können Sie auch Teile der Benutzeroberfläche anpassen.

Wenn Sie mehr Flexibilität benötigen als das Widget bietet, sollten Sie die Query API verwenden. Informationen zum Erstellen einer Suchoberfläche mit der Query API finden Sie unter Mit der Query API eine Suchoberfläche erstellen.

Suchoberfläche erstellen

Zum Erstellen der Suchoberfläche sind mehrere Schritte erforderlich:

  1. Suchanwendung konfigurieren
  2. Client-ID für die Anwendung generieren
  3. HTML-Markup für das Suchfeld und die Ergebnisse hinzufügen
  4. Widget auf die Seite laden
  5. Widget initialisieren

Suchanwendung konfigurieren

Für jede Suchoberfläche muss in der Admin-Konsole eine Suchanwendung definiert sein. Die Suchanwendung enthält zusätzliche Informationen für die Abfrage, z. B. Datenquellen, Attribute und Einstellungen für die Suchqualität.

Informationen zum Erstellen einer Suchanwendung finden Sie unter Benutzerdefinierte Suche erstellen.

Client-ID für die Anwendung generieren

Zusätzlich zu den Schritten unter Zugriff auf die Google Cloud Search API konfigurieren müssen Sie auch eine Client-ID für die Webanwendung generieren.

Projekt konfigurieren

Wenn Sie das Projekt konfigurieren, geschieht Folgendes:

  • Wählen Sie als Clienttyp Webbrowser aus.
  • Geben Sie den Ursprungs-URI Ihrer App an.
  • Notieren Sie sich die erstellte Client-ID. Für die nächsten Schritte benötigen Sie die Client-ID. Der Clientschlüssel ist für das Widget nicht erforderlich.

Weitere Informationen finden Sie unter OAuth 2.0 für clientseitige Webanwendung.

HTML-Markup hinzufügen

Damit das Widget funktioniert, ist etwas HTML-Code erforderlich. Sie müssen Folgendes angeben:

  • Ein input-Element für das Suchfeld.
  • Ein Element, mit dem das Pop-up mit Vorschlägen verankert werden soll.
  • Ein Element, das die Suchergebnisse enthält.
  • (Optional) Geben Sie ein Element für die Attributsteuerelemente an.

Das folgende HTML-Snippet zeigt den HTML-Code für ein Such-Widget, wobei die zu bindenden Elemente durch ihr id-Attribut gekennzeichnet sind:

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>

Widget laden

Das Widget wird dynamisch über ein Loader-Skript geladen. Wenn Sie das Ladeprogramm einbinden möchten, verwenden Sie das Tag <script> wie hier gezeigt:

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>

Im Script-Tag müssen Sie einen onload-Callback angeben. Die Funktion wird aufgerufen, wenn das Ladeprogramm bereit ist. Wenn das Ladeprogramm bereit ist, fahren Sie mit dem Laden des Widgets fort. Rufen Sie dazu gapi.load() auf, um den API-Client, Google Log-in und die Cloud Search-Module zu laden.

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

Die Funktion initializeApp() wird aufgerufen, nachdem alle Module geladen wurden.

Widget initialisieren

Initialisieren Sie zuerst die Clientbibliothek, indem Sie gapi.client.init() oder gapi.auth2.init() mit der generierten Client-ID und dem Bereich https://www.googleapis.com/auth/cloud_search.query aufrufen. Verwenden Sie als Nächstes die Klassen gapi.cloudsearch.widget.resultscontainer.Builder und gapi.cloudsearch.widget.searchbox.Builder, um das Widget zu konfigurieren und an Ihre HTML-Elemente zu binden.

Das folgende Beispiel zeigt, wie das Widget initialisiert wird:

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

Im obigen Beispiel wird für die Konfiguration auf zwei Variablen verwiesen, die wie folgt definiert sind:

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/...";

Anmeldung anpassen

Standardmäßig werden Nutzer vom Widget aufgefordert, sich anzumelden und die App zu autorisieren, wenn sie mit der Eingabe einer Abfrage beginnen. Mit Google Log-in für Websites können Sie Ihren Nutzern eine individuellere Anmeldung anbieten.

Nutzer direkt autorisieren

Verwenden Sie Über Google anmelden, um den Anmeldestatus des Nutzers zu überwachen und Nutzer nach Bedarf an- oder abzumelden. Im folgenden Beispiel wird der isSignedIn-Status beobachtet, um Anmeldeänderungen zu überwachen. Dabei wird die Methode GoogleAuth.signIn() verwendet, um die Anmeldung über einen Klick auf eine Schaltfläche zu initiieren:

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

Weitere Informationen finden Sie unter Über Google anmelden.

Nutzer automatisch anmelden

Sie können die Anmeldung weiter optimieren, indem Sie die Anwendung im Namen von Nutzern in Ihrer Organisation vorab autorisieren. Diese Technik ist auch nützlich, wenn Sie Cloud Identity-Aware Proxy verwenden, um die Anwendung zu schützen.

Weitere Informationen finden Sie im Hilfeartikel Google Log-in mit IT-Apps verwenden.

Benutzeroberfläche anpassen

Sie können die Darstellung der Suchoberfläche mithilfe verschiedener Methoden ändern:

  • Stile mit CSS überschreiben
  • Elemente mit einem Adapter dekorieren
  • Benutzerdefinierte Elemente mit einem Adapter erstellen

Stile mit CSS überschreiben

Das Such-Widget hat einen eigenen CSS-Code, mit dem Sie Vorschläge und Ergebniselemente sowie die Steuerelemente für die Paginierung gestalten können. Sie können diese Elemente bei Bedarf neu gestalten.

Während des Ladevorgangs lädt das Such-Widget dynamisch sein Standard-Stylesheet. Dies geschieht, nachdem Anwendungs-Stylesheets geladen wurden, wodurch die Priorität der Regeln erhöht wird. Damit Ihre eigenen Stile Vorrang vor den Standardstilen haben, verwenden Sie Ancestor-Selektoren, um die Standardregeln spezifischer zu machen.

Die folgende Regel hat beispielsweise keine Auswirkungen, wenn sie in einem statischen link- oder style-Tag im Dokument geladen wird.

.cloudsearch_suggestion_container {
  font-size: 14px;
}

Qualifizieren Sie die Regel stattdessen mit der ID oder Klasse des auf der Seite deklarierten Ancestor-Containers.

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

Eine Liste der Supportklassen und Beispiel-HTML, die vom Widget erstellt wurden, finden Sie in der Referenz zu unterstützten CSS-Klassen.

Elemente mit einem Adapter dekorieren

Wenn Sie ein Element vor dem Rendering dekorieren möchten, müssen Sie einen Adapter erstellen und verwalten, der eine der Dekorierungsmethoden wie decorateSuggestionElement oder decorateSearchResultElement. implementiert.

Mit den folgenden Adaptern wird den Vorschlags- und Ergebniselementen beispielsweise eine benutzerdefinierte Klasse hinzugefügt.

serving/widget/public/with_decorated_element/app.js
/**
 * 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');
}

Um den Adapter beim Initialisieren des Widgets zu registrieren, verwenden Sie die Methode setAdapter() der jeweiligen Builder-Klasse:

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();

Mit einem Decorator können die Attribute des Containerelements sowie alle untergeordneten Elemente geändert werden. Untergeordnete Elemente können während der Dekoration hinzugefügt oder entfernt werden. Wenn Sie jedoch strukturelle Änderungen an den Elementen vornehmen, sollten Sie die Elemente direkt erstellen, anstatt sie zu dekorieren.

Benutzerdefinierte Elemente mit einem Adapter erstellen

Wenn Sie ein benutzerdefiniertes Element für einen Vorschlag, einen Attributcontainer oder ein Suchergebnis erstellen möchten, erstellen und registrieren Sie einen Adapter, der createSuggestionElement, createFacetResultElement oder createSearchResultElement entsprechend implementiert.

Die folgenden Adapter veranschaulichen, wie benutzerdefinierte Vorschlags- und Suchergebniselemente mithilfe von HTML-<template>-Tags erstellt werden.

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

Wenn Sie den Adapter beim Initialisieren des Widgets registrieren möchten, verwenden Sie die Methode setAdapter() der entsprechenden Builder-Klasse:

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();

Das Erstellen benutzerdefinierter Attributelemente mit createFacetResultElement unterliegt mehreren Einschränkungen:

  • Sie müssen die CSS-Klasse cloudsearch_facet_bucket_clickable an das Element anhängen, auf das Nutzer klicken, um zwischen Buckets zu wechseln.
  • Sie müssen jeden Bucket in einem Element mit der CSS-Klasse cloudsearch_facet_bucket_container zusammenfassen.
  • Sie können die Buckets nicht in einer anderen Reihenfolge rendern, als sie in der Antwort angezeigt werden.

Das folgende Snippet rendert Facetten beispielsweise mithilfe von Links anstelle von Kästchen.

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) {
  // ...
}

Suchverhalten anpassen

Die Einstellungen der Suchanwendung stellen die Standardkonfiguration für eine Suchoberfläche dar und sind statisch. Wenn Sie dynamische Filter oder Attribute implementieren möchten, z. B. um Nutzern das Ein-/Ausschalten von Datenquellen zu ermöglichen, können Sie die Einstellungen der Suchanwendung überschreiben, indem Sie die Suchanfrage mit einem Adapter abfangen.

Implementieren Sie einen Adapter mit der Methode interceptSearchRequest, um vor der Ausführung an die Search API gesendete Anfragen zu ändern.

Der folgende Adapter fängt beispielsweise Anfragen ab, um Abfragen auf eine vom Nutzer ausgewählte Quelle zu beschränken:

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

Wenn Sie den Adapter beim Initialisieren des Widgets registrieren möchten, verwenden Sie beim Erstellen von ResultsContainer die Methode setAdapter().

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();

Der folgende HTML-Code wird verwendet, um ein Auswahlfeld zum Filtern nach Quellen anzuzeigen:

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>

Der folgende Code überwacht die Änderung, legt die Auswahl fest und führt die Abfrage bei Bedarf noch einmal aus.

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

Sie können die Suchantwort auch abfangen, indem Sie interceptSearchResponse im Adapter implementieren.

API-Version anpinnen

Standardmäßig verwendet das Widget die neueste stabile API-Version. Wenn Sie eine bestimmte Version sperren möchten, legen Sie den Konfigurationsparameter cloudsearch.config/apiVersion vor der Initialisierung des Widgets auf die bevorzugte Version fest.

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

Die API-Version wird standardmäßig auf 1.0 gesetzt, wenn sie nicht konfiguriert oder auf einen ungültigen Wert festgelegt wird.

Widget-Version anpinnen

Um unerwartete Änderungen an Suchoberflächen zu vermeiden, legen Sie den Konfigurationsparameter cloudsearch.config/clientVersion so fest:

gapi.config.update('cloudsearch.config/clientVersion', 1.1);

Wenn die Widget-Version nicht konfiguriert oder auf einen ungültigen Wert festgelegt ist, wird standardmäßig 1.0 verwendet.

Suchoberfläche schützen

Suchergebnisse enthalten streng vertrauliche Informationen. Befolgen Sie die Best Practices zum Sichern von Webanwendungen, insbesondere vor Clickjacking-Angriffen.

Weitere Informationen finden Sie unter OWASP Guide Project

Fehlerbehebung aktivieren

Verwenden Sie interceptSearchRequest, um die Fehlerbehebung für das Such-Widget zu aktivieren. Beispiel:

  if (!request.requestOptions) {
  // Make sure requestOptions is populated
  request.requestOptions = {};
  }
  // Enable debugging
  request.requestOptions.debugOptions = {enableDebugging: true}

  return request;