algolia / dynamic-faceting-instant-search-guide

16 stars 5 forks source link

Can't get this to work, says dynamicFacetsWidget is not defined? #4

Open giojkd opened 4 years ago

giojkd commented 4 years ago

Any help with the object of this issue? Please really need to make a dynamic facets widget

giojkd commented 4 years ago

Found out this is null const helper = this.helper;

giojkd commented 4 years ago

definitely still not working

Haroenv commented 4 years ago

if dynamicFacetsWidget is undefined, I think that means you didn't import or define the custom widget. There's a demo of this technique here you can look at: https://github.com/algolia/dynamic-faceting-instant-search/tree/master/client

giojkd commented 4 years ago

Link brings to 404

Sent from my iPhone

On 12 Oct 2020, at 09:26, Haroen Viaene notifications@github.com wrote:

 if dynamicFacetsWidget is undefined, I think that means you didn't import or define the custom widget. There's a demo of this technique here you can look at: https://github.com/algolia/dynamic-faceting-instant-search/tree/master/client

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

Haroenv commented 4 years ago

sorry, that was a private project, but here's the code of this example:

const APP_ID       = '',
      API_KEY      = '',
      INDEX_NAME   = '',
      FACET_CONFIG = {
        "Structures": { widgetType: "refinementList", displayName: "Structures", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Residential Units": { widgetType: "refinementList", displayName: "Residential Units", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Power Plant Production Capacity (MW)": { widgetType: "refinementList", displayName: "Power Plant Production Capacity (MW)", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Hotel Keys": { widgetType: "refinementList", displayName: "Hotel Keys", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Parking Spots": { widgetType: "refinementList", displayName: "Parking Spots", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Total Length (km)": { widgetType: "refinementList", displayName: "Total Length (km)", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Villa Units": { widgetType: "refinementList", displayName: "Villa Units", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Serviced Apartments": { widgetType: "refinementList", displayName: "Serviced Apartments", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Retail Units": { widgetType: "refinementList", displayName: "Retail Units", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Elevators": { widgetType: "refinementList", displayName: "Elevators", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Hospital Beds": { widgetType: "refinementList", displayName: "Hospital Beds", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Seating Capacity": { widgetType: "refinementList", displayName: "Seating Capacity", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Offices": { widgetType: "refinementList", displayName: "Offices", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Number of Stations": { widgetType: "refinementList", displayName: "Number of Stations", widgetConfig: {}, parentAttribute: "numeric_attributes" },
        "Special Features": { widgetType: "refinementList", displayName: "Special Features", widgetConfig: {}, parentAttribute: "non_numeric_attributes" },
        "Power Plant Type": { widgetType: "refinementList", displayName: "Power Plant Type", widgetConfig: {}, parentAttribute: "non_numeric_attributes" },
        "Hotel Stars": { widgetType: "refinementList", displayName: "Hotel Stars", widgetConfig: {}, parentAttribute: "non_numeric_attributes" },
        "Facade Colour": { widgetType: "refinementList", displayName: "Facade Colour", widgetConfig: {}, parentAttribute: "non_numeric_attributes" },
        "Facade Material": { widgetType: "refinementList", displayName: "Facade Material", widgetConfig: {}, parentAttribute: "non_numeric_attributes" },
        "Architectural Style": { widgetType: "refinementList", displayName: "Architectural Style", widgetConfig: {}, parentAttribute: "non_numeric_attributes" },
        "Sustainability Rating": { widgetType: "refinementList", displayName: "Sustainability Rating", widgetConfig: {}, parentAttribute: "non_numeric_attributes" }
      },
      MAX_FACET_DISPLAYED = 10;

const search = instantsearch({
  appId: APP_ID,
  apiKey: API_KEY,
  indexName: INDEX_NAME,
  urlSync: true,
  searchParameters: {
    disjunctiveFacets: ['dynamic_attributes']
  },
  searchFunction: function(h) {
    const helper = this.helper;

    // After changing the query, reset active facets
    helper.setState(helper.state.setFacets(['']));

    helper.searchOnce({hitsPerPage:0}).then(function(params) {
      const content = params.content;
      if(content.disjunctiveFacets) {
        const newFacetsArr = content.getFacetValues('dynamic_attributes');

        // Sort facets array by count DESC LIMIT 10 to use for refinementLists
        let facetsForRefinement = newFacetsArr
                                    .sort((a,b) => b.count - a.count)
                                    .slice(0,MAX_FACET_DISPLAYED)
                                    .map((facetObj) => FACET_CONFIG[facetObj.name].parentAttribute + '.' + facetObj.name);

        // Update helper state to use newly retrieved facets
        helper.setState(helper.state.setFacets(facetsForRefinement));
      }

      h.search();
    });
  },
});

// Main search input
search.addWidget(
  instantsearch.widgets.searchBox({
    container: '#input-container',
    placeholder: 'Search for projects...'
  })
);

// Results list
search.addWidget(
  instantsearch.widgets.hits({
    container: '#results-container',
    templates: {
      item:
        `<div>
          <span class="title">{{{_highlightResult.title.value}}}</span>
          <span class="description">{{{_highlightResult.description.value}}}</span>
        </div>`
    }
  })
);

const refinementListContainer = document.body.querySelector('#refinement-lists');

let dynamicFacetsWidget = {
  getConfiguration: function () {},
  init: function (options) { // This method executes once, when the dynamicFacetsWidget is initialized
    const refineElement = function (el) {
      el.setAttribute('data-refined', "refined");
    }
    const unrefineElement = function (el) {
      el.setAttribute('data-refined', "unrefined");
    }

    const _facetClickHandler = function (e) {
      let element = e.target;
      // Only continue if the target of the click is a valid selection
      if (e.target.className !== 'refinement-list-unordered-list-item') {
        return false;
      }
      let type               = element.getAttribute('data-facet-type'),
          name               = element.getAttribute('data-facet-name'),
          value              = element.getAttribute('data-facet-value'),
          facetAttributeName = type + '.' + name;

      if (type === 'non_numeric_attributes') {
        options.helper.toggleRefinement(facetAttributeName,value).search();
        (element.getAttribute('data-refined') === "refined") ? unrefineElement(element) : refineElement(element);
      }

      if (type === 'numeric_attributes') {
        if (element.getAttribute('data-refined') === "refined") {
          // Already refined:
          // (1) Determine type of numeric refinement and removeNumericRefinement
          if (element.textContent.indexOf('-') === -1) {
            // Simple greater-than integer refinement
            options.helper.removeNumericRefinement(facetAttributeName,'>=', parseInt(value,10)).search();
          } else {
            // Mock range-based refinement (actually filters on string)
            options.helper.toggleRefinement(facetAttributeName,value).search();
          }
          // (2) setAttribute to unrefined...this can be used for CSS too
          element.setAttribute('data-refined', "unrefined");
        } else {
          // Not yet refined:
          // (1) Determine type of numeric refinement and addNumericRefinement
          if (element.textContent.indexOf('-') === -1) {
            // Simple greater-than integer refinement
            options.helper.addNumericRefinement(facetAttributeName,'>=', parseInt(value,10)).search();
          } else {
            // Mock range-based refinement (actually filters on string)
            options.helper.toggleRefinement(facetAttributeName,value).search();
          }
          // (2) setAttribute to refined...this can be used for CSS too
          element.setAttribute('data-refined', "refined");
        }
      }
    }

    refinementListContainer.addEventListener('click', _facetClickHandler);
  },
  render: function (options) {
    const content = options.results;
    let facetValues = content.facets.map((facet) => {
      return {
        name: facet.name,
        values: content.getFacetValues(facet.name)
      };
    });

    // Sort facet values by count
    let sortedFacetValues = facetValues.sort((a,b) => (b.values.reduce((p,c) => p + c.count, 0)) - (a.values.reduce((p,c) => p + c.count, 0)));

    // Create array of current refinements as strings
    let currentRefinements = content.getRefinements().map((refinementObj) => {
      return refinementObj.attributeName + '.' + refinementObj.name;
    });

    refinementListContainer.innerHTML = '';

    sortedFacetValues.map((facet,index,array) => {
      let facetParent = facet.name.split('.')[0],
          facetName   = facet.name.split('.')[1];

      let newRefinementList = document.createElement('div'),
          header            = document.createElement('h3'),
          unorderedList     = document.createElement('ul');

      header.textContent = facetName;

      newRefinementList.appendChild(header);
      newRefinementList.appendChild(unorderedList);
      refinementListContainer.appendChild(newRefinementList);

      facet.values.map((value) => {
        let listItem = document.createElement('li');

        listItem.className   = "refinement-list-unordered-list-item";
        listItem.textContent = value.name + ' (' + value.count + ')';

        listItem.setAttribute('data-facet-type',facetParent);
        listItem.setAttribute('data-facet-name',facetName);
        listItem.setAttribute('data-facet-value',value.name);
        listItem.setAttribute('data-facet-count',value.count);

        if (currentRefinements.indexOf(facetParent+'.'+facetName+'.'+value.name) !== -1) {
          // Set data-attribute to indicate that this value is currently refined upon
          listItem.setAttribute('data-refined','refined');
        }

        unorderedList.appendChild(listItem);
      });
    });
  }
};

search.addWidget(dynamicFacetsWidget);
search.start();