ais-hierarchical-menu
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
<ais-hierarchical-menu [attributes]="string[]" // Optional parameters [limit]="number" separator="string" rootPath="string" [showParentLevel]="boolean" [sortBy]="string[]|function" [autoHideContainer]="boolean" [transformItems]="function" ></ais-hierarchical-menu>
1
2
3
4
5
6
7
8
import { NgAisHierarchicalMenuModule } from 'angular-instantsearch';
@NgModule({
imports: [
NgAisHierarchicalMenuModule,
],
})
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-hierarchical-menu
component displays a tree menu that lets users browse attributes.
Requirements
The objects to use in the hierarchical menu must follow this structure:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[
{
"objectID": "321432",
"name": "lemon",
"categories.lvl0": "products",
"categories.lvl1": "products > fruits"
},
{
"objectID": "8976987",
"name": "orange",
"categories.lvl0": "products",
"categories.lvl1": "products > fruits"
}
]
You can also provide more than one path for each level:
1
2
3
4
5
6
7
8
[
{
"objectID": "321432",
"name": "lemon",
"categories.lvl0": ["products", "goods"],
"categories.lvl1": ["products > fruits", "goods > to eat"]
}
]
The attributes passed to the attributes
prop must be declared as Attributes for faceting on the Algolia dashboard) or configured as attributesForFaceting
with the Algolia API.
Examples
1
2
3
4
5
6
7
<ais-hierarchical-menu
[attributes]="[
'categories.lvl0',
'categories.lvl1',
'categories.lvl2'
]"
></ais-hierarchical-menu>
Props
Parameter | Description | ||
---|---|---|---|
attributes
|
type: string[]
Required
The names of the hierarchical attributes that you need to target, in ascending order. To avoid unexpected behavior, you can’t use the same |
||
Copy
|
|||
limit
|
type: number
default: 10
Optional
How many facet values to retrieve. |
||
Copy
|
|||
separator
|
type: string
default: >
Optional
The level separator used in the records. |
||
Copy
|
|||
rootPath
|
type: string
Optional
The path to use if the first level is not the root level. |
||
Copy
|
|||
showParentLevel
|
type: string
default: true
Optional
Whether to show the siblings of the selected parent level of the current refined value. This option doesn’t impact the root level. All root items are always visible. |
||
Copy
|
|||
sortBy
|
type: string[]|function
default: ["name:asc"]
Optional
How to sort refinements. Must be one or more of the following strings:
You can also give a function (with the same signature as the JavaScript |
||
Copy
|
|||
autoHideContainer
|
type: boolean
Optional
Hides the hierarchical menu if there’s no item to display. |
||
Copy
|
|||
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. This is helpful when transforming, removing, or reordering items. In addition, the full |
||
Copy
|
HTML output
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
<div class="ais-HierarchicalMenu">
<ul class="ais-HierarchicalMenu-list ais-HierarchicalMenu-list--lvl0">
<li class="ais-HierarchicalMenu-item ais-HierarchicalMenu-item--parent ais-HierarchicalMenu-item--selected">
<a class="ais-HierarchicalMenu-link" href="#">
<span class="ais-HierarchicalMenu-label">Appliances</span>
<span class="ais-HierarchicalMenu-count">4,306</span>
</a>
<ul class="ais-HierarchicalMenu-list ais-HierarchicalMenu-list--child ais-HierarchicalMenu-list--lvl1">
<li class="ais-HierarchicalMenu-item ais-HierarchicalMenu-item--parent">
<a class="ais-HierarchicalMenu-link" href="#">
<span class="ais-HierarchicalMenu-label">Dishwashers</span>
<span class="ais-HierarchicalMenu-count">181</span>
</a>
</li>
<li class="ais-HierarchicalMenu-item">
<a class="ais-HierarchicalMenu-link" href="#">
<span class="ais-HierarchicalMenu-label">Fans</span>
<span class="ais-HierarchicalMenu-count">91</span>
</a>
</li>
</ul>
</li>
<li class="ais-HierarchicalMenu-item ais-HierarchicalMenu-item--parent">
<a class="ais-HierarchicalMenu-link" href="#">
<span class="ais-HierarchicalMenu-label">Audio</span>
<span class="ais-HierarchicalMenu-count">1,570</span>
</a>
</li>
</ul>
</div>
Customize the UI with connectHierarchicalMenu
If you want to create your own UI of the ais-hierarchical-menu
widget, you can combine the connectHierarchicalMenu
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-hierarchical-menu',
template: '<p>It works!</p>'
})
export class HierarchicalMenu extends TypedBaseWidget {
constructor(
@Inject(forwardRef(() => NgAisIndex))
@Optional()
public parentIndex: NgAisIndex,
@Inject(forwardRef(() => NgAisInstantSearch))
public instantSearchInstance: NgAisInstantSearch
) {
super('HierarchicalMenu');
}
}
There are a couple of things happening in this boilerplate:
- create a
HierarchicalMenu
class extendingTypedBaseWidget
- reference the
<ais-instantsearch>
parent component instance on theHierarchicalMenu
widget class - set
app-hierarchical-menu
as a selector, so we can use our component as<app-hierarchical-menu></app-hierarchical-menu>
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 connectHierarchicalMenu, {
HierarchicalMenuWidgetDescription,
HierarchicalMenuConnectorParams
} from 'instantsearch.js/es/connectors/hierarchical-menu/connectHierarchicalMenu';
@Component({
selector: 'app-hierarchical-menu',
template: '<p>It works!</p>'
})
export class HierarchicalMenu extends TypedBaseWidget<HierarchicalMenuWidgetDescription, HierarchicalMenuConnectorParams> {
public state: HierarchicalMenuWidgetDescription['renderState']; // Rendering options
constructor(
@Inject(forwardRef(() => NgAisIndex))
@Optional()
public parentIndex: NgAisIndex,
@Inject(forwardRef(() => NgAisInstantSearch))
public instantSearchInstance: NgAisInstantSearch
) {
super('HierarchicalMenu');
}
ngOnInit() {
this.createWidget(connectHierarchicalMenu, {
// instance options
attributes: ['categories.lvl0', 'categories.lvl1', 'categories.lvl2'],
});
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: HierarchicalMenuWidgetDescription['renderState'];
// {
// items: object[];
// isShowingMore: boolean;
// canToggleShowMore: boolean;
// refine: Function;
// toggleShowMore: Function;
// createURL: Function;
// widgetParams: object;
// }
1
2
3
4
5
6
7
8
9
10
11
<div *ngFor="let item of state.items">
<!-- level 0 -->
<label>
<input type="checkbox"
(click)="state.refine(item.value)"
[checked]="item.isRefined" > {{ item.label }} ({{ item.count }})
</label>
<div *ngFor="let subitem of item.data">
<!-- level 1 ... -->
</div>
</div>
If SEO is critical to your search page, your custom HTML markup needs to be parsable:
- use plain
<a>
tags withhref
attributes for search engines bots to follow them, - use semantic markup with structured data when relevant, and test it.
Refer to our SEO checklist for building SEO-ready search experiences.
Rendering options
Parameter | Description |
---|---|
items
|
type: object[]
The list of available items, with each item:
|
isShowingMore
|
type: boolean
Whether the list is expanded. |
canToggleShowMore
|
type: boolean
Whether users can click the “Show more” button. |
refine
|
type: function
Sets the path of the hierarchical filter and triggers a new search. |
toggleShowMore
|
type: function
Toggles the number of displayed values between |
createURL
|
type: function
Generates a URL for the next state. |
widgetParams
|
type: object
All original widget options forwarded to the render function. |
Instance options
Parameter | Description |
---|---|
attributes
|
type: string[]
Required
The name of the attributes to generate the menu with. To avoid unexpected behavior, you can’t use the same |
limit
|
type: number
default: 10
Optional
How many facet values to retrieve. When isShowingMore is |
showMoreLimit
|
type: number
Optional
The maximum number of displayed items (only used when the |
separator
|
type: string
default: >
Optional
The level separator used in the records. |
rootPath
|
type: string
default: null
Optional
The prefix path to use if the first level is not the root level. |
showParentLevel
|
type: boolean
default: true
Optional
Whether to show the siblings of the selected parent level of the current refined value. |
sortBy
|
type: string[]|function
default: ["name:asc"]
Optional
How to sort refinements. Must be one or more of the following strings:
You can also give a function, which receives items two by two, like JavaScript’s |
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 |
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
38
39
40
41
42
43
import { Component, Inject, forwardRef, Optional } from '@angular/core';
import { TypedBaseWidget, NgAisInstantSearch, NgAisIndex } from 'angular-instantsearch';
import connectHierarchicalMenu, {
HierarchicalMenuWidgetDescription,
HierarchicalMenuConnectorParams
} from 'instantsearch.js/es/connectors/hierarchical-menu/connectHierarchicalMenu';
@Component({
selector: 'app-hierarchical-menu',
template: `
<div *ngFor="let item of state.items">
<!-- level 0 -->
<label>
<input type="checkbox"
(click)="state.refine(item.value)"
[checked]="item.isRefined" > {{ item.label }} ({{ item.count }})
</label>
<div *ngFor="let subitem of item.data">
<!-- level 1 ... -->
</div>
</div>
`
})
export class HierarchicalMenu extends TypedBaseWidget<HierarchicalMenuWidgetDescription, HierarchicalMenuConnectorParams> {
public state: HierarchicalMenuWidgetDescription['renderState']; // Rendering options
constructor(
@Inject(forwardRef(() => NgAisIndex))
@Optional()
public parentIndex: NgAisIndex,
@Inject(forwardRef(() => NgAisInstantSearch))
public instantSearchInstance: NgAisInstantSearch
) {
super('HierarchicalMenu');
}
ngOnInit() {
this.createWidget(connectHierarchicalMenu, {
// instance options
attributes: ['categories.lvl0', 'categories.lvl1', 'categories.lvl2'],
});
super.ngOnInit();
}
}