Change attributeName dynamically

Good day all!

What would be the “correct” way to change “attributeName” of “refinementList” widget dynamically?

Let’s say I have a select dropdown with 2 options - one “activates” refinement by “Attr1” while other should by “Attr2”? Does it make sense?

Cheers!

I managed to achieve something similar by simply adding new/same widget on select dropdown change, but with different attributeName.

However when I try to “removeWidget” just before adding new one, I get an error: “The widget you tried to remove does not implement the dispose method, therefore it is not possible to remove this widget”.

I’m using refinementList Connector.

Hello,
Have you tried to put the name of your attribute in your state, and instantiate the widget like that?

<RefinementList attribute={this.state.attributeName} />

Hi,

did you mean to somehow change state on a select dropdown change? I’m using vanilla js… So should I use something like:

search.helper.setState()

?

I don’t know the details of your implementation so maybe it’s not relevant in your case, but yes, having the dropdown updating the state was what I had in mind.
Don’t hesitate to share more code if needed!

Thank you!
But does that mean (according to your example), I have to set “attributeName” somehow like this:

search.addWidget(
    customRefinementList({
      containerNode: $('.categories'),
      //attributeName: 'attr1',
      attributeName: this.state.attributeName,
      sortBy: ['name: asc']
    })
);

Obviously this example above doesn’t work, so I’m kinda stuck here.
Trying to use “search.helper” here doesn’t work as well since search has not started yet here and I’m also sure it’s really not how it shold be handled:

attributeName: search.helper.getState().disjunctiveFacets[1]
// I have 2 disjunctiveFacets and I need 2nd as attributeName here

Hi @fuji,

We don’t have dedicated support for the update of widget parameters. The go-to solution for those cases is adding/removing widgets. This is the solution we use inside Vue InstantSearch & Angular InstantSearch, both are built on top of InstantSearch.js.

I saw that you already tried this approach but you got an error. I’ve tried on my side with the connector connectRefinementList and I didn’t manage to reproduce it. Here is the example I built for the reproduction.

Could you provide us some context about this error? With which version of InstantSearch it happens? Or even a live example, it helps a lot to better understand where the problem comes from. We provide a template to avoid you the boilerplate part. You can find it on CodeSandbox.

Thanks!

1 Like

Thank you for the reply! In my original work I use InstantsearchJs 2.10.4, and trying to do something like this here - https://codesandbox.io/s/instantsearchjs-app-seswe?fontsize=14 - rough example.

PS: Now when trying to call “search.removeWidget()” i get an error: “t is not a function”.

PPS: Removing searchBox which is not a connector, but “instantsearch.widgets.searchBox()” works.

Thanks a lot for the example! It helps a lot!

The error raised is expected, it’s not well explained but it’s expected. Each widget built with the connector API is able to provide a second argument in addition to the render function. This function is called the unmount function. Its main purpose is to perform the cleanup of the DOM once the widget is removed. Here is an example:

var customWidget = instantsearch.connectors.connectRefinementList(
  render,
  function unmount() {
    // This function is called once the widget is removed. The main
    // usage is to clean up the DOM on the widget is removed.
  }
);

Inside your example, an error is thrown because this function is not provided. It should not throw an error though, we’ll have to fix this issue on our side. I’ve updated your example with the unmount function implementation.

PS: The error about the attributeName vs attribute is because the CodeSandbox template uses the latest version of InstantSearch (3.4.x). Between the version 2 & 3 the option attributeName has been renamed attribute. You can find the full list of changes inside the migration guide.

2 Likes

Thank you a lot! Works! :sob: :heart:

Could you please take a look at this logic with select dropdown? https://codesandbox.io/s/instantsearchjs-use-the-unmount-function-vhyon?fontsize=14

Some why it stopped working.

Edit: Is it because both widgets have the same “containerNode” which is being deleted on select dropdown change?

The unmount function removes the node where the widgets are attached. Rather than removing the full node you can remove its children only. With jQuery it should be possible with:

$('#brand-list').children().remove();

You can take a look at the updated example, it should behave correctly.

1 Like

I have edited example so it would refine the values, and it works. However not in my original work.

The logic is the same as in the example - but just after selecting other attribute (through select dropdown) I get an error about previous attribute being an undeclared facet (and it depends on the which widget was added first by default):

Uncaught Error: Cannot refine the undeclared facet TESTFACET; it should be added to the helper options facets, disjunctiveFacets or hierarchicalFacets
at a.toggleFacetRefinement (instantsearch.js@2.10.4:2)
at r.toggleFacetRefinement (instantsearch.js@2.10.4:2)
at r.toggleRefinement (instantsearch.js@2.10.4:2)
at Object.C [as refine] (instantsearch.js@2.10.4:2)
at HTMLLIElement. (app.js:17)
at HTMLUListElement.dispatch (jquery-3.3.1.min.js:2)
at HTMLUListElement.y.handle (jquery-3.3.1.min.js:2)

app.js:17:

opt.refine($(this).find(’[data-facet]’).data(‘facet’));

PS: Some why in the example, when filtering by Categories, and picking “Small Kitchen Appliances” - this filter disappears from the list, but still refines the results…

PPS: I tried to add to helper options disjunctiveFacets like this:

search.helper.setState(search.helper.state.setDisjunctiveFacets([‘TESTFACET’, ‘ANOTHERFACET’]));

And error disappeared, but once I try to refine by ANOTHERFACET all the list and hits disappear.

I didn’t understand the issue. Inside the example, I didn’t manage to reproduce the error. The line that you’re pointed at is not inside the CodeSandbox you’ve linked. Could you provide more explanation or an example of the issue?

Some why in the example, when filtering by Categories, and picking “Small Kitchen Appliances” - this filter disappears from the list, but still refines the results…

Yes, this is expected. The engine returns the most relevant facets for the search. Once you’ve refined the value “Small Kitchen Appliances” more relevant facet are retrieved. The widget only displays 10 facets at the same time ordered by name. Which means that if a facet with a name that precedes the current refinement is found, its position shifts to the bottom until it reaches the limit. That’s why by default all our sort contains the isRefined token to always display the selected items.

Hi Samuel,

about the “Small Kitchen Appliances” - so you mean when I filtered hits by “Small Kitchen Appliances”, Algolia kind of re-evaluated hits and replaced facets by new ones retrieved from the re-evaluated hits?

Sorry for being confusing, but example I proved earlier is just an abstract implementation of my original work. I’m afraid I can’t share original work’s code, so I try to reproduce it abstractly in general manner, just simplified general logic through the example. So I implemented everything as in the example - 2 widgets (Brands/Categories), 1st (Brands) being added by default, and then this select dropdown functionality, which removes that Brands widget and creates new one - Categories, then I get those errors about attribute of first widget (Brands) being undeclared facet when I try to refine by Category… - in the example it works, but my original work gets that errors in 13/14 post.

I think I still confusing you, but anyway I went with other, much more simpler solution - displaying 2 widgets at very start but hiding one by CSS, then toggling view of each accordingly to select dropdown option - doesn’t seem right but at least quick :no_mouth: