Tworzenie interfejsu wyszukiwania za pomocą widżetu wyszukiwania

Widżet wyszukiwania zawiera konfigurowalny interfejs wyszukiwania dla aplikacji internetowych. Widżet wymaga niewielkiej ilości kodu HTML i JavaScript do implementować i włączać typowe funkcje wyszukiwania, takie jak aspekty i podział na strony; Ty może też dostosować części interfejsu za pomocą arkuszy CSS i JavaScript.

Jeśli potrzebujesz większej elastyczności niż widżet, rozważ użycie Interfejs Query API. Informacje o tworzeniu interfejsu wyszukiwania za pomocą interfejsu Query API znajdziesz znajdziesz w artykule Tworzenie interfejsu wyszukiwania za pomocą interfejsu Query API.

Tworzenie interfejsu wyszukiwania

Utworzenie interfejsu wyszukiwania składa się z kilku kroków:

  1. Konfigurowanie wyszukiwarki
  2. Wygeneruj identyfikator klienta dla aplikacji
  3. Dodaj znaczniki HTML pola wyszukiwania i wyników
  4. Wczytaj widżet na stronie
  5. Zainicjuj widżet

Konfigurowanie wyszukiwarki

Każdy interfejs wyszukiwania musi mieć zdefiniowaną wyszukiwarkę zdefiniowaną w konsoli administracyjnej. Wyszukiwarka udostępnia dodatkowe informacje związane z zapytaniem, takie jak źródła danych, aspekty, i ustawieniami jakości wyszukiwania.

Aby utworzyć wyszukiwarkę, zapoznaj się z artykułem Utwórz niestandardową wyszukiwarkę.

Wygeneruj identyfikator klienta dla aplikacji

Poza wykonaniem kroków opisanych w Skonfiguruj dostęp do interfejsu Google Cloud Search API. musisz też wygenerować identyfikator klienta dla aplikacji internetowej.

Konfigurowanie projektu

Podczas konfigurowania projektu:

  • Wybierz typ klienta Przeglądarka internetowa.
  • Podaj identyfikator URI źródła Twojej aplikacji.
  • Zanotuj utworzony identyfikator klienta. Będzie Ci potrzebny identyfikator klienta do o wykonanie kolejnych kroków. Tajny klucz klienta nie jest wymagany w przypadku widżet.

Więcej informacji: Protokół OAuth 2.0 dla aplikacji internetowej po stronie klienta

Dodaj znaczniki HTML

Widżet wymaga do działania niewielkiej ilości kodu HTML. Ty musi zawierać:

  • Element input pola wyszukiwania.
  • Element, do którego zakotwiczysz wyskakujące okienko z sugestią.
  • Element do przechowywania wyników wyszukiwania.
  • (Opcjonalnie) Podaj element zawierający elementy sterujące aspektów.

Poniższy fragment kodu HTML zawiera kod HTML widżetu wyszukiwania, w którym element Elementy do powiązania są identyfikowane przez atrybut 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>

Wczytaj widżet

Widżet jest ładowany dynamicznie przez skrypt wczytujący. Do uwzględnienia W programie wczytywania użyj tagu <script> w taki sposób:

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>

Musisz podać wywołanie zwrotne onload w tagu skryptu. Funkcja ta nazywa się gdy moduł ładowania będzie gotowy. Gdy narzędzie będzie gotowe, kontynuuj ładowanie widżetu wywołując metodę gapi.load(), która pozwala wczytać klienta interfejsu API, moduły Logowanie przez Google i 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)
}

Funkcja initializeApp() jest wywoływana, gdy wszystkie moduły są wczytano.

Zainicjuj widżet

Najpierw zainicjuj bibliotekę klienta, wywołując gapi.client.init() lub gapi.auth2.init() za pomocą wygenerowanego identyfikatora klienta i zakresu https://www.googleapis.com/auth/cloud_search.query. Następnie użyj gapi.cloudsearch.widget.resultscontainer.Builder i gapi.cloudsearch.widget.searchbox.Builder klasy do konfigurowania widżetu i powiązać go z elementami HTML.

Poniższy przykład pokazuje, jak uruchomić widżet:

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

W przykładzie powyżej podano 2 zmienne konfiguracji zdefiniowane w taki sposób:

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

Dostosowywanie procesu logowania

Domyślnie widżet prosi użytkowników o zalogowanie się i autoryzowanie aplikacji w chwili, gdy zaczną wpisywać zapytanie. Za pomocą Logowanie przez Google w witrynach aby lepiej dostosować proces logowania do potrzeb użytkowników.

Bezpośrednie autoryzację użytkowników

Użyj funkcji Zaloguj się przez Google, aby monitorować stan logowania użytkownika, a także zalogować lub wylogować użytkowników. Na przykład: przykład zauważa isSignedIn w celu monitorowania zmian logowania oraz korzysta z funkcji GoogleAuth.signIn() do inicjowania logowania za pomocą przycisku kliknij:

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

Więcej informacji znajdziesz w artykule Logowanie się przez Google.

Automatyczne logowanie użytkowników

Aby jeszcze bardziej uprościć logowanie, możesz wstępnie autoryzować w imieniu użytkowników z Twojej organizacji. Ta technika jest przydatne też w przypadku korzystania z Cloud Identity Aware Proxy. aby chronić aplikację.

Więcej informacji znajdziesz w artykule Korzystanie z logowania przez Google w aplikacjach IT.

Dostosowywanie interfejsu

Możesz zmienić wygląd interfejsu wyszukiwania, używając kombinacji tych elementów technik:

  • Zastępowanie stylów CSS
  • Ozdabiaj elementy za pomocą adaptera
  • Tworzenie elementów niestandardowych za pomocą adaptera

Zastępowanie stylów CSS

Widżet wyszukiwania ma własny arkusz CSS do określania stylu sugestii i elementów wyników jak i z elementami sterującymi podziałem na strony. W razie potrzeby możesz zmienić styl tych elementów.

Podczas wczytywania widżet wyszukiwania dynamicznie wczytuje swój domyślny arkusz stylów. Dzieje się tak po wczytaniu arkuszy stylów aplikacji, co zwiększa priorytet zasad. Aby mieć pewność, że Twoje własne style będą miały pierwszeństwo przed stylami domyślnymi, użyj selektorów elementów nadrzędnych, aby zwiększyć szczegółowość reguł domyślnych.

Na przykład poniższa reguła nie działa, jeśli jest wczytywana w elemencie statycznym link lub style w dokumencie.

.cloudsearch_suggestion_container {
  font-size: 14px;
}

Zamiast tego kwalifikuj regułę na podstawie identyfikatora lub klasy kontenera nadrzędnego zadeklarowany na stronie.

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

Listę klas pomocy i przykładowego kodu HTML wygenerowanego przez widżet znajdziesz w sekcji Dokumentacja obsługiwanych klas CSS.

Ozdabiaj elementy za pomocą adaptera

Aby udekorować element przed wyrenderowaniem, utwórz i ponownie zarejestruj umożliwiający zastosowanie jednej z metod dekorowania, takich jak decorateSuggestionElement lub decorateSearchResultElement.

Na przykład te adaptery dodają do sugestii klasę niestandardową i elementów wyników.

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

Aby zarejestrować adapter podczas inicjowania widżetu, użyj narzędzia setAdapter() odpowiedniej klasy Builder:

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

Dekoratory mogą modyfikować atrybuty elementu kontenera oraz elementów podrzędnych. Podczas dekorowania można dodawać i usuwać elementy podrzędne. Jeśli jednak wprowadzisz zmiany strukturalne elementów, rozważ utworzenie elementów bezpośrednio zamiast dekorowania.

Tworzenie elementów niestandardowych za pomocą adaptera

Aby utworzyć element niestandardowy dla sugestii, kontenera aspektu lub wyniku wyszukiwania: utworzyć i zarejestrować adapter, który implementuje createSuggestionElement, createFacetResultElement, lub createSearchResultElement traktowanie jakby nie było miłością.

Poniższe adaptery ilustrują tworzenie niestandardowych sugestii i wyników wyszukiwania elementy korzystające z tagów 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;
}

Aby zarejestrować adapter podczas inicjowania widżetu, użyj komponentu setAdapter() odpowiedniej klasy Builder:

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

Tworzenie elementów aspektów niestandardowych za pomocą funkcji createFacetResultElement podlegają kilku ograniczeniom:

  • Musisz dołączyć klasę usługi porównywania cen cloudsearch_facet_bucket_clickable do element, który użytkownik klika, aby przełączyć zasobnik.
  • Musisz opakować każdy zasobnik w element nadrzędny za pomocą atrybutu CSS zajęcia cloudsearch_facet_bucket_container.
  • Nie możesz wyrenderować zasobników w innej kolejności niż występują w .

Na przykład ten fragment renderuje aspekty za pomocą linków pola wyboru.

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

Dostosowywanie działania wyszukiwania

Ustawienia wyszukiwarki odpowiadają domyślnym ustawieniom dla interfejsu wyszukiwania i są statyczne. Aby wdrożyć kreacje dynamiczne, filtry lub aspekty, np. aby umożliwić użytkownikom przełączanie źródeł danych, zastąpić ustawienia wyszukiwarki przez przechwycenie żądania wyszukiwania z przejściówką.

Zaimplementuj przejściówkę z interceptSearchRequest do modyfikowania żądań wysyłanych do interfejs Search API przed wykonaniem tych czynności.

Na przykład poniższy adapter przechwytuje żądania, aby ograniczyć zapytania do źródła wybranego przez użytkownika:

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

Aby zarejestrować adapter podczas inicjowania widżetu, użyj setAdapter() podczas tworzenia ResultsContainer

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

Następujący kod HTML służy do wyświetlenia pola wyboru, według którego można filtrować źródła:

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>

Następujący kod nasłuchuje zmiany, ustawia wybór oraz w razie potrzeby ponownie wykonuje zapytanie.

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

Możesz też przechwytywać odpowiedź wyszukiwania, implementując interceptSearchResponse w przejściu.

Przypnij wersję interfejsu API

Domyślnie widżet używa najnowszej stabilnej wersji interfejsu API. Aby zablokować określonej wersji, ustaw parametr konfiguracji cloudsearch.config/apiVersion lub wybrać preferowaną wersję przed jego zainicjowaniem.

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

Jeśli zasada jest nieskonfigurowana lub ustawiona na nieprawidłową wartość, wersja interfejsu API przyjmuje domyślnie wartość 1.0.

Przypnij wersję widżetu

Aby uniknąć nieoczekiwanych zmian w interfejsach wyszukiwania, ustaw parametr cloudsearch.config/clientVersion, jak pokazano poniżej:

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

Jeśli nie jest skonfigurowana lub zostanie ustawiona na nieprawidłową wartość, wersja widżetu będzie domyślnie ustawiona na 1,0.

Zabezpieczanie interfejsu wyszukiwania

Wyniki wyszukiwania zawierają informacje poufne. Stosowanie sprawdzonych metod do zabezpieczania aplikacji internetowych, zwłaszcza przed ataki typu clickjacking.

Więcej informacji znajdziesz w przewodniku OWASP Guide (w języku angielskim).

Włącz debugowanie

Użyj formatu interceptSearchRequest aby włączyć debugowanie widżetu wyszukiwania. Na przykład:

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

  return request;