Integrations / Platforms / Magento 2 / Front-End Custom Events

Front-End Custom Events

Autocomplete menu events

You can adjust all the logic of the autocomplete.js integration by registering a custom method in your JS file. Registering of a hook can be done by using algolia JS object.

You can learn how to add a custom JS file in a Create a custom extension tutorial.

Possible hooks:

  • beforeAutocompleteSources(sources, algoliaClient, algoliaBundle)
    • can be used to modify default data sources
    • the hook must return sources variable
  • beforeAutocompleteOptions(options)
    • can be used to modify default options of autocomplete menu
    • the hook must return options variable

These hooks are triggered right before the autocomplete feature initializes.

Example of the hooks:

1
2
3
4
5
6
7
8
9
algolia.registerHook('beforeAutocompleteSources', function(sources, algoliaClient, algoliaBundle)  {
  // Add or modify sources, then return them
  return sources;
});

algolia.registerHook('beforeAutocompleteOptions', function(options) {
  // Modify options, then return them
  return options;
});
  • afterAutocompleteStart(algoliaAutocompleteInstance)
    • can be used to observe events on the autocomplete element
    • the hook must return algoliaAutocompleteInstance variable

Example of the hook:

1
2
3
4
5
// Bind new event on the autocomplete element
algolia.registerHook('afterAutocompleteStart', function(algoliaAutocompleteInstance) {
  // Modify default autocomplete instance, then return it
  return algoliaAutocompleteInstance;
});

Instant search page events

You can adjust all the logic of the InstantSearch.js integration by registering a couple of custom hooks:

  • beforeInstantsearchInit(instantsearchOptions, algoliaBundle)
  • beforeWidgetInitialization(allWidgetConfiguration, algoliaBundle)
    • can be used to add/remove/modify any widget(s)
  • beforeInstantsearchStart(search, algoliaBundle)
  • afterInstantsearchStart(search, algoliaBundle)

By registering these hook(s) in your JavaScript file, you can directly modify their parameters which must be returned back from the method.

Example of the beforeInstantsearchInit(instantsearchOptions, algoliaBundle) hook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Modify default `instantsearchOptions`
algolia.registerHook('beforeInstantsearchInit', function(instantsearchOptions, algoliaBundle) {

  // Adding a custom Query Rule context
  var newQueryRuleContext = 'new-custom-query-rule-context';
  instantsearchOptions.searchParameters.ruleContexts.push(newQueryRuleContext);
  // see other possible searchParameters: https://www.algolia.com/doc/api-reference/api-parameters/

  // Example of an after search callback
  var callbackSearchFunction = instantsearchOptions.searchFunction;
  instantsearchOptions.searchFunction = function(helper) {

    // Add your `searchFunction` methods here

    // Run the previous `searchFunction`
    callbackSearchFunction(helper);
  }

  return instantsearchOptions;
});

Example on how to add a new toggleRefinement widget to instant search page:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
algolia.registerHook('beforeWidgetInitialization', function(allWidgetConfiguration) {
  const wrapper = document.getElementById('instant-search-facets-container');

  const widgetConfig = {
    container: wrapper.appendChild(createISWidgetContainer('in_stock')),
    attributeName: 'in_stock',
    label: 'In Stock',
    values: {
      on: 1
    },
    templates: {
      header: '<div class="name">Is in stock</div>'
    }
  };

  if (typeof allWidgetConfiguration['toggle'] === 'undefined') {
    allWidgetConfiguration['toggle'] = [widgetConfig];
  } else {
    allWidgetConfiguration['toggle'].push(widgetConfig);
  }

  return allWidgetConfiguration;
});

All default widgets can be found in allWidgetConfiguration object and can be removed or modified in this method.

Insights events

You can add new events for Click and Conversion Analytics and Personalization by registering this custom hook:

  • afterInsightsBindEvents(algoliaInsights)
    • can be used to add new events
    • algoliaInsights gives you access to methods for tracking:
      • trackClick(eventData)
      • trackView(eventData)
      • trackConversion(eventData)
    • to format eventData for insights you can use:
      • buildEventData(eventName, objectID, indexName, position = null, queryID = null)
      • click and conversion analytics requires the optional parameters for position and queryID

Example of a custom click event for personalization:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
algolia.registerHook('afterInsightsBindEvents', function(algoliaInsights) {
    
    var selectors = document.querySelectorAll('.class-selector');
    selectors.forEach(function (e) {
        e.addEventListener('click', function (event) {

            // selector in this example has an data-objectid attribute
            // with the objectID as the value
            var objectId = this.dataset.objectid;

            // use the buildEventData function to format event data
            var eventData = algoliaInsights.buildEventData(
                'Clicked Event Name', // eventName
                objectId, // objectID
                algoliaConfig.indexName + '_products' // indexName
            );

            algoliaInsights.trackClick(eventData);

        });
    });

});

Example usages

You can use this section as inspiration for your customization. These examples are not definitive and will need to be adjusted to suit your needs.

Add category sorting in the autocomplete menu

In this example, we will create a new section that displays categories that are returned by the product results. Please read our guide on creating a custom extension to learn more about templating in this section.

In order to retrieve the categories that are returned by a product search, we need to create a new section that queries our product index. When we define the source of this section, we have to pass in the facets parameter that returns the categories attribute which the extension indexes as: categories.level0, categories.level2, etc..

The search will return product results as hits, so we have to render the facets instead of products. This can be done in the template suggestions function. Make sure that your template variables match those returned by the facets in order to render them in autocomplete.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
algolia.registerHook('beforeAutocompleteSources', function (sources, algolia_client, algoliaBundle) {

  algoliaConfig.autocomplete.templates['NAME OF AUTOCOMPLETE TEMPLATE'] = algoliaBundle.Hogan.compile(jQuery('#autocomplete_catprod_template').html());

  var categoryProductsSource = {
    source: algoliaBundle.autocomplete.sources.hits(algolia_client.initIndex('NAME OF PRODUCT INDEX'), {
      hitsPerPage: 2,
      analyticsTags: 'autocomplete',
      facets: ['categories.level0, categories.level2'],
      maxValuesPerFacet: 3
    }),
    displayKey: 'value',
    name: sources.length + 1,
    templates: {
      header: '<div class="category">Category w/ Products </div>',
      empty: '<div class="aa-no-results">' + algoliaConfig.translations.noResults + '</div>',
      suggestion: function (hit, payload) {

        hit.__queryID = payload.queryID;
        hit.__position = payload.hits.indexOf(hit) + 1;

        var facets = (hit.facets);

        if (hit.facets) {
          algoliaConfig.autocomplete.templates['NAME OF AUTOCOMPLETE TEMPLATE'].render(hit.facets);
        }
      }
    }
  };

  sources.push(categoryProductsSource);
  return sources;
  
});

To add a new filter, we have to modify the section’s search parameters to include the new filter condition. To do this, we have to recreate the source with our updated options.

In the example below, we are appending a new numericFilter to the search parameters, in_stock=1. Because we are recreating the source value, we need to recreate the options passed into the product source as defined in common.js. In the numericFilters, we will return an array of conditions that will include our new condition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
algolia.registerHook('beforeAutocompleteSources', function(sources, algoliaClient, algoliaBundle) {
    
  for (i = 0; i < sources.length; i++) {
    if (sources[i].name == 'products') {

      var productSection = algoliaConfig.autocomplete.sections.filter(function(section) {
        if (section.name == 'products') {
          return section;
        }
      });

      // recreate the options from common.js
      var productOptions = {
        hitsPerPage: productSection[0].hitsPerPage,
        analyticsTags: 'autocomplete',
        clickAnalytics: true,
        facets: ['categories.level0'],
        numericFilters: ['visibility_search=1', 'in_stock=1'] // add filter options
      };

      sources[i].source = algoliaBundle.autocomplete.sources.hits(algolia_client.initIndex(algoliaConfig.indexName + '_products'), productOptions);
      
    }
  }
  
  return sources;

});

Add custom rules context to InstantSearch

If you have to add new contexts for some of your custom rules, please use the specified front-end hook for your InstantSearch version. We recommend you to append your new context to the preconfigured ones added by the extension, as follows:

1
2
3
4
5
6
7
algolia.registerHook('beforeInstantsearchInit', function(instantsearchOptions, algoliaBundle) {
  // Adding a custom Query Rule context
  var newQueryRuleContext = 'new-custom-query-rule-context';
  instantsearchOptions.searchParameters.ruleContexts.push(newQueryRuleContext);

  return instantsearchOptions;
});

Sort facet values with InstantSearch

By default, facet values displayed for a search are sorted by the number of matching results. However, this can be changed. In the following example, we will sort the size facet values based on the size values. To do this, we need to hook into the event beforeWidgetInitialization to modify the facet for size.

To sort the values in the refinementList based on size, we are going to add the sortBy function to our refinementList. This function will allow you to match values against each other and sort each value.

To determine the order, we will use the orderedSizes array variable defined in the code below. This can be pulled from any source, like Magento or from another index if needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
algolia.registerHook('beforeWidgetInitialization', function(allWidgetConfiguration, algoliaBundle) {
  
  var orderedSizes = ['XXS', 'XS', 'S', 'M', 'L', 'XL', 'XXL'];

  for (var i = 0; i < allWidgetConfiguration.refinementList.length; i++) {
    if (allWidgetConfiguration.refinementList[i].attribute == 'size') {
      allWidgetConfiguration.refinementList[i].sortBy = function(a, b) {
        return orderedSizes.indexOf(a.name) - orderedSizes.indexOf(b.name);
      };
    }
  }

  return allWidgetConfiguration;

});

Did you find this page helpful?