Crea una interfaz de búsqueda con el widget de búsqueda

El widget de búsqueda proporciona una interfaz de búsqueda que se puede personalizar para aplicaciones web. Requiere una cantidad mínima de código HTML y JavaScript para implementarse, y admite funciones comunes, como facetas y paginación. También puedes personalizar la interfaz con CSS y JavaScript.

Si necesitas más flexibilidad, usa la API de Query. Consulta Cómo crear una interfaz de búsqueda con la API de Query.

Compila una interfaz de búsqueda

Para compilar la interfaz de búsqueda, debes seguir estos pasos:

  1. Configura una aplicación de búsqueda.
  2. Genera un ID de cliente para la aplicación.
  3. Agrega el lenguaje de marcado HTML para el cuadro de búsqueda y los resultados.
  4. Carga el widget en la página.
  5. Inicializa el widget.

Configura una aplicación de búsqueda

Cada interfaz de búsqueda requiere una aplicación de búsqueda definida en la Consola del administrador. La aplicación proporciona parámetros de configuración de la búsqueda, como fuentes de datos, facetas y parámetros de calidad de la búsqueda.

Para crear una aplicación de búsqueda, consulta Crea una experiencia de búsqueda personalizada.

Genera un ID de cliente para la aplicación

Además de los pasos que se indican en Configura el acceso a la API de Cloud Search, genera un ID de cliente para tu aplicación web.

Configura un proyecto

Cuando configures el proyecto, haz lo siguiente:

  • Selecciona el tipo de cliente Navegador web.
  • Proporciona la URI de origen de tu app.
  • Toma nota del ID de cliente. El widget no requiere un secreto del cliente.

Si deseas obtener más información, consulta OAuth 2.0 para la aplicación web del lado del cliente.

Agrega el lenguaje de marcado HTML

El widget requiere los siguientes elementos HTML:

  • Un elemento input para el cuadro de búsqueda.
  • Es un elemento para fijar el diálogo de sugerencias.
  • Es un elemento para los resultados de la búsqueda.
  • Un elemento para los controles de facetas (opcional).

En este fragmento, se muestran elementos identificados por sus atributos 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>

Carga el widget

Incluye el cargador con una etiqueta <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>

Proporciona una devolución de llamada onload. Cuando el cargador esté listo, llama a gapi.load() para cargar el cliente de API, el Acceso con Google y los módulos de 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)
}

Inicializa el widget

Inicializa la biblioteca cliente con gapi.client.init() o gapi.auth2.init() con tu ID de cliente y el alcance https://www.googleapis.com/auth/cloud_search.query. Usa las clases de compilador para configurar y vincular el widget.

Ejemplo de inicialización:

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 configuración:

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

Personaliza la experiencia de acceso

El widget solicita a los usuarios que accedan cuando comienzan a escribir. Puedes usar el Acceso con Google para sitios web y disfrutar de una experiencia personalizada.

Autoriza a los usuarios directamente

Usa Acceder con Google para supervisar y administrar los estados de acceso. En este ejemplo, se usa GoogleAuth.signIn() en un clic de botón:

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

Acceso automático para los usuarios

Autoriza previamente la aplicación para los usuarios de tu organización y, así, optimiza el acceso. Esto también es útil con Cloud Identity Aware Proxy. Consulta Usa Acceder con Google en apps de TI.

Personaliza la interfaz

Puedes cambiar la apariencia del widget de las siguientes maneras:

  • Anular los estilos con CSS
  • Decorar elementos con un adaptador
  • Crea elementos personalizados con un adaptador.

Cómo anular los estilos con CSS

El widget incluye su propio CSS. Para anularlo, usa selectores principales para aumentar la especificidad:

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

Consulta la referencia de las clases de CSS compatibles.

Decora elementos con un adaptador

Crea y registra un adaptador para modificar elementos antes de la renderización. En este ejemplo, se agrega una clase de CSS personalizada:

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

Registra el adaptador durante la inicialización:

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

Crea elementos personalizados con un adaptador

Implementa createSuggestionElement, createFacetResultElement o createSearchResultElement para compilar componentes de IU personalizados. En este ejemplo, se usan etiquetas <template> de HTML:

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

Registra el adaptador:

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

Los elementos de faceta personalizados deben seguir estas reglas:

  • Adjunta cloudsearch_facet_bucket_clickable a los elementos en los que se puede hacer clic.
  • Encierra cada discretización en un cloudsearch_facet_bucket_container.
  • Mantén el orden de los buckets de la respuesta.

Por ejemplo, el siguiente fragmento procesa facetas mediante vínculos, en vez de casillas de verificación.

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

Personaliza el comportamiento de la búsqueda

Anula la configuración de la aplicación de búsqueda interceptando solicitudes con un adaptador. Implementa interceptSearchRequest para modificar las solicitudes antes de la ejecución. En este ejemplo, se restringen las consultas a una fuente seleccionada:

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

Registra el adaptador:

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

El siguiente código HTML se usa a fin de mostrar un cuadro de selección para filtrar por fuentes:

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>

El siguiente código escucha el cambio, establece la selección y vuelve a ejecutar la consulta si es necesario.

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

También puedes interceptar la respuesta de búsqueda implementando interceptSearchResponse en el adaptador.

Cómo fijar versiones

  • Versión de API: Establece cloudsearch.config/apiVersion antes de inicializar.
  • Versión del widget: Usa gapi.config.update('cloudsearch.config/clientVersion', 1.1).

Si no se configuran, ambos parámetros se establecen en 1.0 de forma predeterminada.

Por ejemplo, para fijar el widget en la versión 1.1, haz lo siguiente:

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

Asegura la interfaz de búsqueda

Sigue las prácticas recomendadas de seguridad para aplicaciones web, especialmente para evitar el clickjacking.

Cómo habilitar la depuración

Usa interceptSearchRequest para habilitar la depuración:

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