Créer une interface de recherche avec le widget Recherche

Le widget Recherche propose une interface de recherche personnalisable pour les applications Web. Sa mise en œuvre nécessite peu de code HTML et JavaScript, et il prend en charge les fonctionnalités courantes telles que les facettes et la pagination. Vous pouvez également personnaliser l'interface avec CSS et JavaScript.

Si vous avez besoin de plus de flexibilité, utilisez l'API Query. Consultez Créer une interface de recherche avec l'API Query.

Créer une interface de recherche

La procédure de création d'une interface de recherche comprend les étapes suivantes :

  1. Configurez une application de recherche.
  2. Générez un ID client pour l'application.
  3. Ajoutez un balisage HTML pour le champ de recherche et les résultats.
  4. Chargez le widget sur la page.
  5. Initialisez le widget.

Configurer une application de recherche

Une application de recherche doit être définie dans la console d'administration pour chaque interface de recherche. L'application fournit des paramètres de requête, tels que les sources de données, les facettes et les paramètres de qualité de la recherche.

Pour créer une application de recherche, consultez Créer une expérience de recherche personnalisée.

Générer un ID client pour l'application

Outre les étapes décrites sur la page Configurer l'accès à l'API Cloud Search, générez un ID client pour votre application Web.

Configurer un projet

Lors de la configuration du projet :

  • Sélectionnez le type de client Navigateur Web.
  • indiquez l'URI d'origine de votre application ;
  • Notez l'ID client. Le widget ne nécessite pas de code secret client.

Pour en savoir plus, consultez OAuth 2.0 pour les applications Web côté client.

Ajouter un balisage HTML

Le widget nécessite les éléments HTML suivants :

  • Un élément input pour le champ de recherche.
  • Un élément qui servira de point d'ancrage pour la boîte de dialogue de suggestion.
  • Un élément pour les résultats de recherche.
  • (Facultatif) Un élément pour les commandes d'attribut.

Cet extrait montre des éléments identifiés par leurs attributs 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>

Charger le widget

Incluez le chargeur à l'aide d'une balise <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>

Fournissez un rappel onload. Lorsque le chargeur est prêt, appelez gapi.load() pour charger le client API, Google Sign-In et les modules 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)
}

Initialiser le widget

Initialisez la bibliothèque cliente à l'aide de gapi.client.init() ou gapi.auth2.init() avec votre ID client et le champ d'application https://www.googleapis.com/auth/cloud_search.query. Utilisez les classes de création pour configurer et associer le widget.

Exemple d'initialisation :

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

Variables de configuration :

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

Personnaliser l'expérience de connexion

Le widget invite les utilisateurs à se connecter lorsqu'ils commencent à saisir du texte. Vous pouvez utiliser Google Sign-In for Websites pour une expérience personnalisée.

Autoriser les utilisateurs directement

Utilisez Se connecter avec Google pour surveiller et gérer les états de connexion. Cet exemple utilise GoogleAuth.signIn() lors d'un clic sur un bouton :

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

Connecter automatiquement les utilisateurs

Pré-autorisez l'application pour les utilisateurs de votre organisation afin de simplifier la connexion. Cette technique s'avère également utile avec Cloud Identity-Aware Proxy. Consultez Utiliser Se connecter avec Google avec des applications informatiques.

Personnaliser l'interface

Vous pouvez modifier l'apparence du widget :

  • Remplacer les styles avec du code CSS
  • Décorer des éléments avec un adaptateur.
  • Créer des éléments personnalisés avec un adaptateur.

Remplacer les styles avec du code CSS

Le widget inclut son propre CSS. Pour le remplacer, utilisez des sélecteurs d'ancêtre afin d'accroître la spécificité :

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

Consultez la documentation de référence sur les classes CSS compatibles.

Décorer des éléments avec un adaptateur

Créez et enregistrez un adaptateur pour modifier les éléments avant le rendu. Cet exemple ajoute une classe CSS personnalisée :

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

Enregistrez l'adaptateur lors de l'initialisation :

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

Créer des éléments personnalisés avec un adaptateur

Implémentez createSuggestionElement, createFacetResultElement ou createSearchResultElement pour créer des composants d'UI personnalisés. Cet exemple utilise les balises 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;
}

Enregistrez l'adaptateur :

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

Les éléments de facette personnalisés doivent respecter les règles suivantes :

  • Associez cloudsearch_facet_bucket_clickable aux éléments cliquables.
  • Encapsulez chaque bucket dans un cloudsearch_facet_bucket_container.
  • Conservez l'ordre des buckets de la réponse.

L'extrait de code suivant permet d'afficher les facettes sous forme de liens et non de cases à cocher.

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

Personnaliser le comportement de recherche

Remplacez les paramètres de l'application de recherche en interceptant les requêtes avec un adaptateur. Implémentez interceptSearchRequest pour modifier les requêtes avant leur exécution. Cet exemple limite les requêtes à une source sélectionnée :

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

Enregistrez l'adaptateur :

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

Le code HTML suivant permet d'afficher une zone de sélection pour le filtrage par sources :

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>

Le code suivant écoute la modification, définit la sélection et exécute à nouveau la requête si besoin.

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

Vous pouvez également intercepter la réponse en ajoutant interceptSearchResponse à l'adaptateur.

Épingler des versions

  • Version de l'API : définissez cloudsearch.config/apiVersion avant l'initialisation.
  • Version du widget : utilisez gapi.config.update('cloudsearch.config/clientVersion', 1.1).

Si elles ne sont pas définies, la valeur par défaut est 1.0 pour les deux.

Par exemple, pour épingler le widget à la version 1.1 :

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

Sécuriser l'interface de recherche

Suivez les bonnes pratiques de sécurité pour les applications Web, en particulier pour éviter le détournement de clic.

Activer le débogage

Utilisez interceptSearchRequest pour activer le débogage :

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