Ivano Guerini is a Salesforce Senior Developer at Webresults (Engineering Group) since 2015.
He started my career on Salesforce during his university studies and based his final thesis on it.
He’s passionate about technology and development, in his spare time he enjoys developing applications mainly on Node.js.
The <datalist> element is a new tag available in the HTML5.
This element can be used to create native autocomplete dropdowns without using complex JS and DOM manipulation for data filtering.
As you may have experienced, autocomplete picklist is a usefull component commonly used in forms to facilitate users in finding the correct value in case of very large lists of values.
In this article post, you’re going to learn how to use the datalist element to create a autocomplete dropdowns as Lightning Web Component.
For the TL;DR guys over there, this is the repo.
Let’s get started.
First let’s see how it works in an HTML page.
Simply write this code in an HTML page and we obtain a autocomplete dropdown like the below.
<input list="countries">
<datalist id="countries">
<option>Italy</option>
<option>Spain</option>
<option>France</option>
<option>USA</option>
<option>England</option>
<option>Belgium</option>
<option>Brazil</option>
<option>Mauritius</option>
<option>Colombia</option>
<option>Portugal</option>
<option>Russia</option>
<option>Mauritania</option>
</datalist>
Simple like that, all without any JS code.
Now let’s try to do the same in Salesforce.
Open your favorite IDE, and create a new LWC component, naming it ‘autocomplete’.
The html template we report the same code written above.
<template>
<input id="input" name="input" list="countries" class="slds-input" type="text" />
<datalist id="countries">
<option>Italy</option>
<option>Spain</option>
<option>France</option>
<option>USA</option>
<option>England</option>
<option>Belgium</option>
<option>Brazil</option>
<option>Mauritius</option>
<option>Colombia</option>
<option>Portugal</option>
<option>Russia</option>
<option>Mauritania</option>
</datalist>
</template>
If we try to execute this component, we will see that it does not work as we would expect.
This is because the link between the input and the datalit is managed through the Id attribute. But as Salesforce reminds us:
The IDs that you define in HTML templates may be transformed into globally unique values when the template is rendered. If you use an ID selector in JavaScript, it won’t match the transformed ID.
To overcome this problem we can take advantage of a few lines of JS code, hooking up to the rerenderCallback.
Then in the Javascript controller we write the following function:
renderedCallback() {
let listId = this.template.querySelector('datalist').id;
this.template.querySelector("input").setAttribute("list", listId);
}
This code simply searches for our Datalist element and retrieves the ID generated by Salesforce, and consequently updates the input list attribute with the new Value.
Again the rendered callback can run a lot of times, but our code must be executed only once. To do it we can use a private attribute to know if the renderedCallback has been already executed:
initialized = false;
renderedCallback() {
if (this.initialized) {
return;
}
this.initialized = true;
let listId = this.template.querySelector('datalist').id;
this.template.querySelector("input").setAttribute("list", listId);
}
Now our LWC component will work as autocomplete dropdown.
Let’s evolve it a bit, using dinamic options and decorating it with a label ad other attributes.
The HTML template will tranform like this:
<template>
<label class="slds-form-element__label" for="input">
<template if:true={required}>
<abbr class="slds-required" title="required">* </abbr>
</template>
{label}
</label>
<div class="slds-form-element__control">
<input id="input" name="input" list="valueList" placeholder={placeholder} required={required} class="slds-input" type="text" />
<datalist id="valueList" class="">
<template for:each={values} for:item='item'>
<option key={item.key} value={item.value}>{item.value}</option>
</template>
</datalist>
</div>
</template>
In the JS controller we handle this values with @api decorator.
import { LightningElement, api } from 'lwc';
export default class Autocomplete extends LightningElement {
@api values;
@api label = '';
@api name = '';
@api required;
@api placeholder = '';
initialized = false;
renderedCallback() {
if (this.initialized) {
return;
}
this.initialized = true;
let listId = this.template.querySelector('datalist').id;
this.template.querySelector("input").setAttribute("list", listId);
}
}
Full repo here.