🎉 Try the public beta of the new docs site at algolia.com/doc-beta! 🎉
UI libraries / Angular InstantSearch / Widgets

Angular InstantSearch isn’t compatible with Angular’s Ivy view engine. We’re investigating how best to support this. For more information and to vote for Algolia’s support of Angular 16 and beyond, see the GitHub issue Algolia Support for Angular InstantSearch

Signature
<ais-refinement-list
  attribute="string"

  // Optional parameters
  operator="or|and"
  [limit]="number"
  [showMoreLimit]="number"
  showMoreLabel="string"
  showLessLabel="string"
  [searchable]="boolean"
  searchPlaceholder="string"
  [sortBy]="string[]|function"
  [autoHideContainer]="boolean"
  [transformItems]="function"
></ais-refinement-list>
Import
1
2
3
4
5
6
7
8
import { NgAisRefinementListModule } from 'angular-instantsearch';

@NgModule({
  imports: [
    NgAisRefinementListModule,
  ],
})
export class AppModule {}

1. Follow additional steps in Optimize build size to ensure your code is correctly bundled.
2. This imports all the widgets, even the ones you don’t use. Read the Getting started guide for more information.

About this widget

The ais-refinement-list component displays a list that let the end user choose multiple values for a specific facet.

Requirements

The attribute passed to the attribute prop must be present in “attributes for faceting” on the Algolia dashboard or configured as attributesForFaceting through a set settings call to the Algolia API.

If you are using the searchable prop, you’ll also need to make the attribute searchable using the dashboard or using the API.

Examples

1
<ais-refinement-list attribute="categories"></ais-refinement-list>

Props

Parameter Description
attribute
type: string
Required

The name of the attribute in the record.

To avoid unexpected behavior, you can’t use the same attribute prop in a different type of widget.

1
2
3
<ais-refinement-list
  attribute="categories"
></ais-refinement-list>
operator
type: string
default: or|and
Optional

How to apply refinements.

1
2
3
4
<ais-refinement-list
  // ...
  operator="and"
></ais-refinement-list>
limit
type: number
default: 10
Optional

How many facet values to retrieve.

1
2
3
4
<ais-refinement-list
  // ...
  [limit]="5"
></ais-refinement-list>
showMoreLimit
type: number
Optional

The maximum number of items to displayed when the list is showing more items.

1
2
3
4
<ais-refinement-list
  // ...
  [showMoreLimit]="20"
></ais-refinement-list>
showMoreLabel
type: string
default: Show more
Optional

Label of the “Show more” button.

1
2
3
4
<ais-refinement-list
  // ...
  showMoreLabel="See more"
></ais-refinement-list>
showLessLabel
type: string
default: Show less
Optional

Label of the show less button.

1
2
3
4
<ais-refinement-list
  // ...
  showLessLabel="See less"
></ais-refinement-list>
searchable
type: boolean
default: false
Optional

You should set it to true if the component should display an input to search for facet values. To make this feature work, you need to make the attribute searchable using the API or the dashboard.

In some situations, refined facet values might not be present in the data returned by Algolia. Read the FAQ to get more information and adjust your configuration.

1
2
3
4
<ais-refinement-list
  // ...
  [searchable]="true"
></ais-refinement-list>
searchPlaceholder
type: string
default: Search here...
Optional

Label for the placeholder of the search box.

1
2
3
4
<ais-refinement-list
  // ...
  searchPlaceholder="Search for brand..."
></ais-refinement-list>
sortBy
type: string[]|function
default: ["isRefined", "count:desc", "name:asc"]
Optional

How to sort refinements. Must be one or more of the following strings:

  • "count" (same as "count:desc")
  • "count:asc"
  • "count:desc"
  • "name" (same as "name:asc")
  • "name:asc"
  • "name:desc"
  • "isRefined" (same as "isRefined:asc")
  • "isRefined:asc"
  • "isRefined:desc"

It’s also possible to give a function, which must have the same signature than the JavaScript Array.sort function.

In some situations, refined facet values might not be present in the data returned by Algolia. Read the FAQ to get more information and adjust your configuration.

1
2
3
4
<ais-refinement-list
  // ...
  [sortBy]="['isRefined', 'name:asc']"
></ais-refinement-list>
autoHideContainer
type: boolean
Optional

Hides the refinement list if there’s no item to display.

1
2
3
4
<ais-refinement-list
  // ...
  [autoHideContainer]="true"
></ais-refinement-list>
transformItems
type: function
default: items => items
Optional

Receives the items and is called before displaying them. Should return a new array with the same shape as the original array. Useful for transforming, removing, or reordering items.

In addition, the full results data is available, which includes all regular response parameters, as well as parameters from the Algolia search helper (for example disjunctiveFacetsRefinements).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component({
  template: `
    <ais-refinement-list
      // ...
      [transformItems]="transformItems"
    ></ais-refinement-list>
  `,
})
export class AppComponent {
  transformItems(items) {
    return items.map(item => ({
      ...item,
      highlighted: item.highlighted.toUpperCase(),
    }));
  },

  /* or, combined with results */
  transformItems(items, { results }) {
    return results.page === 0
      ? items.slice(0, 5)
      : items;
  },
}

HTML output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="ais-RefinementList">
  <div class="ais-RefinementList-searchBox">
    <!-- SearchBox widget here -->
  </div>
  <ul class="ais-RefinementList-list">
    <li class="ais-RefinementList-item ais-RefinementList-item--selected">
      <label class="ais-RefinementList-label">
        <input class="ais-RefinementList-checkbox" type="checkbox" value="Insignia™" checked />
        <span class="ais-RefinementList-labelText">Insignia™</span>
        <span class="ais-RefinementList-count">746</span>
      </label>
    </li>
    <li class="ais-RefinementList-item">
      <label class="ais-RefinementList-label">
        <input class="ais-RefinementList-checkbox" type="checkbox" value="Samsung">
        <span class="ais-RefinementList-labelText">Samsung</span>
        <span class="ais-RefinementList-count">633</span>
      </label>
    </li>
  </ul>
  <button class="ais-RefinementList-showMore">Show more</button>
</div>

Customize the UI with connectRefinementList

If you want to create your own UI of the ais-refinement-list widget, you can combine the connectRefinementList connector with the TypedBaseWidget class.

1. Extend the TypedBaseWidget class

First of all, you will need to write some boilerplate code to initialize correctly the TypedBaseWidget class. This happens in the constructor() of your class extending the TypedBaseWidget class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Component, Inject, forwardRef, Optional } from '@angular/core';
import { TypedBaseWidget, NgAisInstantSearch, NgAisIndex } from 'angular-instantsearch';

@Component({
  selector: 'app-refinement-list',
  template: '<p>It works!</p>'
})
export class RefinementList extends TypedBaseWidget {
  constructor(
    @Inject(forwardRef(() => NgAisIndex))
    @Optional()
    public parentIndex: NgAisIndex,
    @Inject(forwardRef(() => NgAisInstantSearch))
    public instantSearchInstance: NgAisInstantSearch
  ) {
    super('RefinementList');
  }
}

There are a couple of things happening in this boilerplate:

  • create a RefinementList class extending TypedBaseWidget
  • reference the <ais-instantsearch> parent component instance on the RefinementList widget class
  • set app-refinement-list as a selector, so we can use our component as <app-refinement-list></app-refinement-list>

2. Connect your custom widget

The TypedBaseWidget class has a method called createWidget() which takes two arguments: the connector to use and an object of options (instance options) for this connector. We call this method at ngOnInit. This component now implements OnInit.

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
import { Component, Inject, forwardRef, Optional } from '@angular/core';
import { TypedBaseWidget, NgAisInstantSearch, NgAisIndex } from 'angular-instantsearch';

import connectRefinementList, {
  RefinementListWidgetDescription,
  RefinementListConnectorParams
} from 'instantsearch.js/es/connectors/refinement-list/connectRefinementList';

@Component({
  selector: 'app-refinement-list',
  template: '<p>It works!</p>'
})
export class RefinementList extends TypedBaseWidget<RefinementListWidgetDescription, RefinementListConnectorParams> {
  public state: RefinementListWidgetDescription['renderState']; // Rendering options
  constructor(
    @Inject(forwardRef(() => NgAisIndex))
    @Optional()
    public parentIndex: NgAisIndex,
    @Inject(forwardRef(() => NgAisInstantSearch))
    public instantSearchInstance: NgAisInstantSearch
  ) {
    super('RefinementList');
  }
  ngOnInit() {
    this.createWidget(connectRefinementList, {
      // instance options
      attribute: 'brands',
    });
    super.ngOnInit();
  }
}

3. Render from the state

Your component instance has access to a this.state property which holds the rendering options of the widget.

public state: RefinementListWidgetDescription['renderState'];
// {
//   items: object[];
//   refine: Function;
//   createURL: Function;
//   isFromSearch: boolean;
//   searchForItems: Function;
//   isShowingMore: boolean;
//   canToggleShowMore: boolean;
//   toggleShowMore: Function;
//   widgetParams: object;
// }
1
2
3
4
5
<label *ngFor="let item of state.items">
  <input type="checkbox"
         (click)="state.refine(item.value)"
         [checked]="item.isRefined" > {{ item.label }} ({{ item.count }})
</label>

Rendering options

Parameter Description
items
type: object[]

The list of refinement values returned from the Algolia API.

Each object has the following properties:

  • value: string: the value of the item
  • label: string: the label of the item
  • count: number: the number of results that match the item
  • isRefined: boolean: whether the refinement is applied
refine
type: function

Toggles a refinement.

createURL
type: function

Generates a URL for the corresponding search state.

isFromSearch
type: boolean

Whether the items prop contains facet values from the global search or from the search inside the items.

searchForItems
type: function

Triggers a search inside items values.

To make this feature work, you need to make the attribute searchable using the dashboard or using the searchable modifier of attributesForFaceting with the API.

isShowingMore
type: boolean

Whether the menu is displaying all the menu items.

canToggleShowMore
type: boolean

Whether the “Show more” button can be activated (if there are enough items to display and not already displaying more than the limit).

toggleShowMore
type: function

Toggles the number of displayed values between limit and showMoreLimit.

widgetParams
type: object

All original widget options forwarded to the render function.

Instance options

Parameter Description
attribute
type: string
Required

The name of the attribute in the records.

To avoid unexpected behavior, you can’t use the same attribute prop in a different type of widget.

operator
type: string ("or"|"and")
default: "or"
Optional

How to apply refinements.

  • "or": apply an OR between all selected values
  • "and": apply an AND between all selected values
limit
type: number
default: 10
Optional

How many facet values to retrieve. When isShowingMore is false, this is the number of facet values displayed before clicking the “Show more” button.

showMoreLimit
type: number
Optional

The maximum number of items to display if the widget is showing more items. Needs to be bigger than the limit parameter.

escapeFacetValues
type: boolean
default: true
Optional

When true, escapes the facet values that are returned from Algolia. In this case, the surrounding tags are always mark.

sortBy
type: string[]|function
default: ["isRefined","count:desc","name:asc"]
Optional

How to sort refinements. Must be one or more of the following strings:

  • "count:asc"
  • "count:desc"
  • "name:asc"
  • "name:desc"
  • "isRefined"

It’s also possible to give a function, which receives items two by two, like JavaScript’s Array.sort.

In some situations, refined facet values might not be present in the data returned by Algolia. Read the FAQ to get more information and adjust your configuration.

transformItems
type: function
Optional

Receives the items and is called before displaying them. Should return a new array with the same shape as the original array. Useful for transforming, removing, or reordering items.

In addition, the full results data is available, which includes all regular response parameters, as well as parameters from the helper (for example disjunctiveFacetsRefinements).

Full example

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
35
36
37
import { Component, Inject, forwardRef, Optional } from '@angular/core';
import { TypedBaseWidget, NgAisInstantSearch, NgAisIndex } from 'angular-instantsearch';

import connectRefinementList, {
  RefinementListWidgetDescription,
  RefinementListConnectorParams
} from 'instantsearch.js/es/connectors/refinement-list/connectRefinementList';

@Component({
  selector: 'app-refinement-list',
  template: `
<label *ngFor="let item of state.items">
  <input type="checkbox"
         (click)="state.refine(item.value)"
         [checked]="item.isRefined" > {{ item.label }} ({{ item.count }})
</label>
`
})
export class RefinementList extends TypedBaseWidget<RefinementListWidgetDescription, RefinementListConnectorParams> {
  public state: RefinementListWidgetDescription['renderState']; // Rendering options
  constructor(
    @Inject(forwardRef(() => NgAisIndex))
    @Optional()
    public parentIndex: NgAisIndex,
    @Inject(forwardRef(() => NgAisInstantSearch))
    public instantSearchInstance: NgAisInstantSearch
  ) {
    super('RefinementList');
  }
  ngOnInit() {
    this.createWidget(connectRefinementList, {
      // instance options
      attribute: 'brands',
    });
    super.ngOnInit();
  }
}
Did you find this page helpful?