Create a search component in lwc

HTML Template

1. searchComponent.html

<template>
    <lightning-card title="Search and Select Contacts">
        <div class="slds-m-around_medium">
            <div class="slds-box slds-box_small slds-theme_default">
                <div class="slds-grid slds-grid_align-spread">
                    <div>
                        <h2>Selected Contacts</h2>
                    </div>
                </div>
                <div class="slds-pill_container slds-m-around_x-small slds-grid slds-grid_align-spread">
                    <div>
                        <template if:true={selectedContacts.length}>
                            <template for:each={selectedContacts} for:item="contact" for:index="index">
                                <span key={contact.Id} class="slds-pill">
                                    <span class="slds-pill__label">{contact.Name}</span>
                                    <button class="slds-button slds-button_icon slds-pill__remove" title="Remove" onclick={handleRemove} data-id={contact.Id}>
                                        <lightning-icon icon-name="utility:close" alternative-text="Remove"></lightning-icon>
                                    </button>
                                </span>
                            </template>
                        </template>
                    </div>
                    <div class="slds-grid slds-grid_align-end slds-m-left_auto">
                        <span class="slds-badge slds-m-right_small">{selectedContacts.length}</span>
                        <lightning-button-icon icon-name="utility:down" onclick={toggleDropdown} alternative-text="Show/Hide List"></lightning-button-icon>
                    </div>
                </div>
                <template if:true={showDropdown}>
                    <div class="slds-m-around_x-small slds-grid slds-grid_align-spread slds-m-bottom_small">
                        <lightning-input 
                            class="slds-col slds-grow full-width" 
                            label="Search" 
                            value={searchTerm} 
                            onchange={handleSearchChange}>
                        </lightning-input>
                        <lightning-button-icon icon-name="utility:search" alternative-text="Search" class="lookup-icon"></lightning-button-icon>
                    </div>
                    <div class="slds-m-around_x-small">
                        <template if:true={filteredContacts.length}>
                            <template for:each={filteredContacts} for:item="contact" for:index="index">
                                <div key={contact.Id} class="slds-box slds-box_x-small slds-m-around_x-small">
                                    <lightning-input type="checkbox" label={contact.Name} checked={contact.isSelected} data-id={contact.Id} onchange={handleCheckboxChange}></lightning-input>
                                </div>
                            </template>
                        </template>
                        <template if:false={filteredContacts.length}>
                            <p>No contacts found</p>
                        </template>
                    </div>
                </template>
            </div>
        </div>
    </lightning-card>
</template>

Custom CSS

Create or update the CSS file for the component to ensure the search input takes the full width and the overall layout is clean.

2. searchComponent.css

.full-width {
    width: 100%;
}

.lookup-icon {
    position: relative;
    top: 1.5rem;
    margin-left: -2.5rem;
}

.slds-m-around_x-small {
    margin: 0.5rem 0;
}

Final JavaScript Controller

3. searchComponent.js

Ensure the JavaScript controller correctly manages the state of selected contacts and filtered contacts.

import { LightningElement, track } from 'lwc';
import searchContacts from '@salesforce/apex/ContactController.searchContacts';
import getSelectedContacts from '@salesforce/apex/ContactController.getSelectedContacts';

export default class SearchComponent extends LightningElement {
    @track searchTerm = '';
    @track selectedContactIds = ['003xxxxxxxxxxxx', '003xxxxxxxxxxxx']; // Add default selected contact IDs here
    @track selectedContacts = [];
    @track filteredContacts = [];
    @track showDropdown = false;

    connectedCallback() {
        this.loadSelectedContacts();
    }

    loadSelectedContacts() {
        getSelectedContacts({ contactIds: this.selectedContactIds })
            .then(result => {
                this.selectedContacts = result;
                this.updateFilteredContactsSelection();
            })
            .catch(error => {
                console.error('Error fetching selected contacts:', error);
            });
    }

    handleSearchChange(event) {
        this.searchTerm = event.target.value;
        if (this.searchTerm.length > 2) {
            this.searchContacts();
        } else {
            this.filteredContacts = [];
        }
    }

    searchContacts() {
        searchContacts({ searchTerm: this.searchTerm })
            .then(result => {
                this.filteredContacts = result.map(contact => ({
                    ...contact,
                    isSelected: this.selectedContactIds.includes(contact.Id)
                }));
            })
            .catch(error => {
                console.error('Error fetching contacts:', error);
            });
    }

    toggleDropdown() {
        this.showDropdown = !this.showDropdown;
    }

    handleCheckboxChange(event) {
        const contactId = event.target.dataset.id;
        const isChecked = event.target.checked;

        if (isChecked) {
            const selectedContact = this.filteredContacts.find(contact => contact.Id === contactId);
            if (!this.selectedContacts.some(contact => contact.Id === contactId)) {
                this.selectedContacts = [...this.selectedContacts, selectedContact];
                this.selectedContactIds = [...this.selectedContactIds, contactId];
            }
        } else {
            this.selectedContacts = this.selectedContacts.filter(contact => contact.Id !== contactId);
            this.selectedContactIds = this.selectedContactIds.filter(id => id !== contactId);
        }

        this.updateFilteredContactsSelection();
    }

    handleRemove(event) {
        const contactId = event.target.dataset.id;
        this.selectedContacts = this.selectedContacts.filter(contact => contact.Id !== contactId);
        this.selectedContactIds = this.selectedContactIds.filter(id => id !== contactId);
        this.updateFilteredContactsSelection();
    }

    updateFilteredContactsSelection() {
        this.filteredContacts = this.filteredContacts.map(contact => ({
            ...contact,
            isSelected: this.selectedContactIds.includes(contact.Id)
        }));
    }
}

Step 3: Deploy the Component and Apex Controller

Deploy your component and Apex controller to your Salesforce org.

sfdx force:source:push

Step 4: Add the Component to a Lightning Page

  1. Navigate to the App Builder in Salesforce.
  2. Add the searchComponent to your desired page.
  3. Save and activate the page.

This final design ensures that the search input, selected contacts, and dropdown are well-organized, providing a clean and user-friendly interface.

Published by Sandeep Kumar

He is a Salesforce Certified Application Architect having 11+ years of experience in Salesforce.

Leave a Reply