Implement a custom component
Implement a custom component
This is for:
DeveloperWhen possible, we recommend that you create your component in TypeScript (see the Introduction to JSUI Custom Components course).
For more information on why it’s considered a good practice, see Create custom components.
When using the Coveo JavaScript Search Framework, you will sometimes want to add your own custom components (see Create custom components).
In this tutorial, you will be adding a rudimentary custom search box to your page using pure JavaScript. This component will execute a query every time a user selects Enter. It will also have a configurable option to enable search-as-you-type, meaning that a query will be sent every time the user types something when the option is enabled.
Step 1: Create your constructor
First, you need to declare a constructor function that the framework will call to instantiate your custom search box component:
const CustomSearchbox = (function(_super) {
__extends(CustomSearchbox, Coveo.Component);
function CustomSearchbox(element, options, bindings) {
_super.call(this, element, CustomSearchbox.ID, bindings);
this.type = 'CustomSearchBox';
Coveo.Component.bindComponentToElement(element, this);
this.element = element;
this.options = options;
this.bindings = bindings;
}
})(Coveo.Component);
The framework will automatically pass the following arguments when it calls this function:
-
element
: the DOM element on which the component is being instantiated. -
options
: the options that were passed for the component in the init top-level function call.The options argument contains options passed as follows:
Coveo.init(document.querySelector('#search'), { CustomSearchbox: { 'foo': 'bar' } })
It does not contain options passed as
data-
attribute values in the component markup (for example,div class='CustomSearchbox' data-foo='bar'></div>
) -
bindings
: an object that references different singletons which are used by the framework. This object has the following properties:-
root
: the DOM element on which the main SearchInterface component is bound. -
queryStateModel
: the State object instance, which can be used to read/write in the state of the search interface. -
queryController
: the QueryController object instance, which can be used to execute queries in the search interface. -
searchInterface
: the mainSearchInterface
component instance. -
usageAnalytics
: the Analytics component instance, which can log various analytics events.
-
Step 2: Add the component ID
In order for your component to work, it needs an ID property that uniquely identifies the component. This property allows the Coveo JavaScript Search Framework to recognize an element with the CoveoCustomSearchBox
class to be instantiated as a component.
Add the following line after the constructor function:
CustomSearchbox.ID = 'CustomSearchbox';
Step 3: Initialize the component
In order for your component to be registered and initialized alongside the Coveo JavaScript Search Framework, you need to call your constructor in the registerAutoCreateComponent
function.
Add the following line after the constructor function:
Coveo.Initialization.registerAutoCreateComponent(CustomSearchbox);
Your code should now look like this:
CustomComponent.js
const CustomSearchbox = (function(_super) {
__extends(CustomSearchbox, Coveo.Component);
function CustomSearchbox(element, options, bindings) {
_super.call(this, element, CustomSearchbox.ID, bindings);
this.type = 'CustomSearchBox';
Coveo.Component.bindComponentToElement(element, this);
this.element = element;
this.options = options;
this.bindings = bindings;
}
CustomSearchbox.ID = 'CustomSearchbox';
Coveo.Initialization.registerAutoCreateComponent(CustomSearchbox);
})(Coveo.Component);
Coveo JavaScript Search Framework 2.2900.23 (July 2017)
When using the Coveo JavaScript Search Framework 2.x, you can also register your component as a lazy component. This way, your code is only initialized when you have the component on your page (see Lazy versus eager component loading).
Step 4: Add a function to trigger a query when the enter key is pressed
As of now, your component is able to be successfully initialized, but it doesn’t do anything.
Since you’re creating a search box, a good function to add would be to trigger a query when the Enter key is selected in the search box.
-
In the constructor, add an event listener on the
keyup
event. Make it so that it calls ahandleKeyUp
function.this.element.addEventListener('keyup', (e)=> this.handleKeyUp(e));
-
Create the
handleKeyUp
function. It should only detect if the released key was Enter, and call theexecuteNewQuery
function when it is.CustomSearchbox.prototype.handleKeyUp = function(e) { if (e.key == 'Enter') { this.executeNewQuery(); } }
-
Create the
executeNewQuery
function. The function should read the value entered in your component, and set it as the new query.CustomSearchbox.prototype.executeNewQuery = function() { this.bindings.queryStateModel.set('q', this.element.value); this.bindings.queryController.executeQuery(); }
-
In the
executeNewQuery
function, send a Usage Analytics event when the query is performed. This is so you can track what your users search (see Why You Should Add Usage Analytics Events).CustomSearchbox.prototype.executeNewQuery = function() { this.bindings.queryStateModel.set('q', this.element.value); this.bindings.usageAnalytics.logSearchEvent({ name : 'submitSearchbox', type : 'CustomSearchbox' }); this.bindings.queryController.executeQuery(); }
Your code should now look like this:
const CustomSearchbox = (function(_super) {
__extends(CustomSearchbox, Coveo.Component);
function CustomSearchbox(element, options, bindings) {
_super.call(this, element, CustomSearchbox.ID, bindings);
this.type = 'CustomSearchBox';
Coveo.Component.bindComponentToElement(element, this);
this.element = element;
this.options = options;
this.bindings = bindings;
this.element.addEventListener('keyup', (e) => this.handleKeyUp(e));
}
CustomSearchbox.prototype.handleKeyUp = function(e) {
if (e.key == 'Enter') {
this.executeNewQuery();
}
}
CustomSearchbox.prototype.executeNewQuery = function() {
this.bindings.queryStateModel.set('q', this.element.value);
this.bindings.usageAnalytics.logSearchEvent({
name: 'submitSearchbox',
type: 'CustomSearchbox'
});
this.bindings.queryController.executeQuery();
}
CustomSearchbox.ID = 'CustomSearchbox';
Coveo.Initialization.registerAutoCreateComponent(CustomSearchbox);
})(Coveo.Component);
Why You Should Add Usage Analytics Events
To get consistent data and reporting inside Coveo Usage Analytics (Coveo UA), it’s mandatory that every search event be logged to the service. Otherwise, the framework will log a warning every time a query is submitted without first having logged a search event.
If you don’t log any search events when you trigger a query, in the your browser console, you will most probably see a warning like this one:
A search was triggered, but no analytics event was logged. If you want to have consistent analytics data, consider logging a search event using the methods provided by the framework
In a Google Chrome browser, the warning looks like this:
Step 5: Add a search-as-you-type option
You can add options to your custom component. Those options can be modified using the data-
HTML attribute (see ComponentOptions).
A nice option to have on a search box is the search-as-you-type
function, which triggers a query while the user is typing, allowing them to preview the results while typing their query.
The search-as-you-type
feature can significantly increase your query count. If you’re close to exceeding your allowed queries per month (QPM), contact your Coveo Sales representative before enabling it.
-
In the constructor, change the
this.options = options
line to initialize your other options (see ComponentOptions - initComponentOptions).this.options = Coveo.ComponentOptions.initComponentOptions(element, CustomSearchbox, options);
-
Declare a
searchAsYouType
option on your component. You want this option to be a Boolean (see ComponentOptions - buildBooleanOption). For this example, you can make its default valuefalse
.CustomSearchbox.options = { searchAsYouType : Coveo.ComponentOptions.buildBooleanOption({defaultValue: false}) }
-
Change your
handleKeyUp
function so that, whensearchAsYouType
is enabled, a query is performed every time a key is released instead of only when theEnter
key is.CustomSearchbox.prototype.handleKeyUp = function(e) { if (this.options.searchAsYouType) { this.executeNewQuery(); } else if (e.key == 'Enter') { this.executeNewQuery(); } }
Your code should now look like this:
const CustomSearchbox = (function(_super) {
__extends(CustomSearchbox, Coveo.Component);
function CustomSearchbox(element, options, bindings) {
_super.call(this, element, CustomSearchbox.ID, bindings);
this.type = 'CustomSearchBox';
Coveo.Component.bindComponentToElement(element, this);
this.element = element;
this.options = Coveo.ComponentOptions.initComponentOptions(element, CustomSearchbox, options);
this.bindings = bindings;
this.element.addEventListener('keyup', (e) => this.handleKeyUp(e));
}
CustomSearchbox.prototype.handleKeyUp = function(e) {
if (this.options.searchAsYouType) {
this.executeNewQuery();
} else if (e.key == 'Enter') {
this.executeNewQuery();
}
}
CustomSearchbox.prototype.executeNewQuery = function() {
this.bindings.queryStateModel.set('q', this.element.value);
this.bindings.usageAnalytics.logSearchEvent({
name : 'submitSearchbox',
type : 'CustomSearchbox'
});
this.bindings.queryController.executeQuery();
}
CustomSearchbox.options = {
searchAsYouType : Coveo.ComponentOptions.buildBooleanOption({defaultValue: false})
}
CustomSearchbox.ID = 'CustomSearchbox';
Coveo.Initialization.registerAutoCreateComponent(CustomSearchbox);
})(Coveo.Component);
You can now add data-search-as-you-type='true'
to your CoveoCustomSearchbox
element to enable your option.
<input class="CoveoCustomSearchbox" data-search-as-you-type='true'></input>
Step 6: Add the newly created component in a search page
Now that you have completed your rudimentary custom search box, you’re ready to add it to your page:
</head>
<script>
// You could of course instead insert a script resource in the header of your search page.
const CustomSearchbox = (function(_super) {
__extends(CustomSearchbox, Coveo.Component);
function CustomSearchbox(element, options, bindings) {
_super.call(this, element, CustomSearchbox.ID, bindings);
this.type = 'CustomSearchBox';
Coveo.Component.bindComponentToElement(element, this);
this.element = element;
this.options = Coveo.ComponentOptions.initComponentOptions(element, CustomSearchbox, options);
this.bindings = bindings;
this.element.addEventListener('keyup', (e) => this.handleKeyUp(e));
}
CustomSearchbox.prototype.handleKeyUp = function(e) {
if (this.options.searchAsYouType) {
this.executeNewQuery();
} else if (e.key == 'Enter') {
this.executeNewQuery();
}
}
CustomSearchbox.prototype.executeNewQuery = function() {
this.bindings.queryStateModel.set('q', this.element.value);
this.bindings.usageAnalytics.logSearchEvent({
name: 'submitSearchbox',
type: 'CustomSearchbox'
});
this.bindings.queryController.executeQuery();
}
CustomSearchbox.options = {
searchAsYouType: Coveo.ComponentOptions.buildBooleanOption({ defaultValue: false })
}
CustomSearchbox.ID = 'CustomSearchbox';
Coveo.Initialization.registerAutoCreateComponent(CustomSearchbox);
})(Coveo.Component);
</script>
[...]
<!-- The class CoveoCustomSearchbox corresponds to the ID of the component -->
<body class='CoveoSearchInterface'>
<input class='CoveoCustomSearchbox' data-search-as-you-type='true'></input>
</body>