src/connectors/autocomplete/connectAutocomplete.js
/**
 * **Autocomplete** connector provides the logic to build a widget that will give the user the ability to search into multiple indices.
 *
 * This connector provides a `refine()` function to search for a query and a `currentRefinement` as the current query used to search.
 *
 * THere's a complete example available on how to write a custom **Autocomplete** widget:
 * [autocomplete.js](https://github.com/algolia/instantsearch.js/blob/develop/dev/app/custom-widgets/jquery/autocomplete.js)
 * @type {Connector}
 * @param {function(AutocompleteRenderingOptions, boolean)} renderFn Rendering function for the custom **Autocomplete** widget.
 * @param {function} unmountFn Unmount function called when the widget is disposed.
 * @return {function(CustomAutocompleteWidgetOptions)} Re-usable widget factory for a custom **Autocomplete** widget.
 */
Autocomplete** connector provides the logic to build a widget that will give the user the ability to search into multiple indices.
This connector provides a `refine()` function to search for a query and a `currentRefinement` as the current query used to search.


Autocomplete ** connector provides the logic to build a widget that will give the user the ability to search into multiple indices . This connector provides a ` refine () ` function to search for a query and a ` currentRefinement ` as the current query used to search .
function connectAutocomplete(renderFn, unmountFn) {
  checkRendering(renderFn, usage);
  return (widgetParams = {}) => {
    const { indices = [] } = widgetParams;
    if (!Array.isArray(indices)) {
      throw new Error(usage);
    }
    return {
      getConfiguration() {
        return widgetParams.escapeHits ? tagConfig : undefined;
      },
      init({ instantSearchInstance, helper }) {
        this._refine = this.refine(helper);
        this.indices = [
          {
            helper,
            label: 'primary',
            index: helper.getIndex(),
            results: undefined,
            hits: [],
          },
        ];
        indices.forEach(({ label, value }) => {
          const derivedHelper = helper.derive(searchParameters =>
            searchParameters.setIndex(value)
          );
          this.indices.push({
            label,
            index: value,
            helper: derivedHelper,
            results: undefined,
            hits: [],
          });
          derivedHelper.on('result', results =>
            this.saveResults({ results, label })
          );
        });
        this.instantSearchInstance = instantSearchInstance;
        this.renderWithAllIndices({ isFirstRendering: true });
      },
      saveResults({ results, label }) {
        const derivedIndex = this.indices.find(i => i.label === label);
        if (
          widgetParams.escapeHits &&
          results.hits &&
          results.hits.length > 0
        ) {
          results.hits = escapeHits(results.hits);
        }
        derivedIndex.results = results;
        derivedIndex.hits =
          results && results.hits && Array.isArray(results.hits)
            ? results.hits
            : [];
        this.renderWithAllIndices();
      },
      refine(helper) {
        return query => helper.setQuery(query).search();
      },
      render({ results }) {
        this.saveResults({ results, label: this.indices[0].label });
      },
      renderWithAllIndices({ isFirstRendering = false } = {}) {
        const currentRefinement = this.indices[0].helper.state.query;
        renderFn(
          {
            widgetParams,
            currentRefinement,
            indices: this.indices.map(({ index, label, hits, results }) => ({
              index,
              label,
              hits,
              results,
            })),
            instantSearchInstance: this.instantSearchInstance,
            refine: this._refine,
          },
          isFirstRendering
        );
      },
      dispose() {
        this.indices.slice(1).forEach(({ helper }) => helper.detach());
        unmountFn();
      },
    };
  };
}
function connectAutocomplete ( renderFn , unmountFn ) { checkRendering ( renderFn , usage ) ; return ( widgetParams = { } ) => { const { indices = [ ] } = widgetParams ; if ( ! Array . isArray ( indices ) ) { throw new Error ( usage ) ; } return { getConfiguration ( ) { return widgetParams . escapeHits ? tagConfig : undefined ; } , init ( { instantSearchInstance , helper } ) { this . _refine = this . refine ( helper ) ; this . indices = [ { helper , label : 'primary' , index : helper . getIndex ( ) , results : undefined , hits : [ ] , } , ] ; indices . forEach ( ( { label , value } ) => { const derivedHelper = helper . derive ( searchParameters => searchParameters . setIndex ( value ) ) ; this . indices . push ( { label , index : value , helper : derivedHelper , results : undefined , hits : [ ] , } ) ; derivedHelper . on ( 'result' , results => this . saveResults ( { results , label } ) ) ; } ) ; this . instantSearchInstance = instantSearchInstance ; this . renderWithAllIndices ( { isFirstRendering : true } ) ; } , saveResults ( { results , label } ) { const derivedIndex = this . indices . find ( i => i . label === label ) ; if ( widgetParams . escapeHits && results . hits && results . hits . length > 0 ) { results . hits = escapeHits ( results . hits ) ; } derivedIndex . results = results ; derivedIndex . hits = results && results . hits && Array . isArray ( results . hits ) ? results . hits : [ ] ; this . renderWithAllIndices ( ) ; } , refine ( helper ) { return query => helper . setQuery ( query ) . search ( ) ; } , render ( { results } ) { this . saveResults ( { results , label : this . indices [ 0 ] . label } ) ; } , renderWithAllIndices ( { isFirstRendering = false } = { } ) { const currentRefinement = this . indices [ 0 ] . helper . state . query ; renderFn ( { widgetParams , currentRefinement , indices : this . indices . map ( ( { index , label , hits , results } ) => ( { index , label , hits , results , } ) ) , instantSearchInstance : this . instantSearchInstance , refine : this . _refine , } , isFirstRendering ) ; } , dispose ( ) { this . indices . slice ( 1 ) . forEach ( ( { helper } ) => helper . detach ( ) ) ; unmountFn ( ) ; } , } ; } ; }
**Autocomplete** connector provides the logic to build a widget that will give the user the ability to search into multiple indices.
** Autocomplete ** connector provides the logic to build a widget that will give the user the ability to search into multiple indices .
// user passed a wrong `indices` option type // add additionnal indices into `this.indices` // update results then trigger render after a search from any helper // we do not want to provide the `helper` to the end-user // detach every derived indices from the main helper instance