LWC Recipe: Searchable Product Table with Table-Only Loading

LWC Table Loading Example

In this quick LWC recipe, you’ll learn how to build a responsive searchable product datatable using a scoped loading spinner that appears only inside the table — keeping the rest of the UI, like the search input, smooth and uninterrupted.

No full-page reloads. No input flicker. Just clean UX.


📦 What You Need


🔧 Apex: ProductService.cls

public with sharing class ProductService {
    @AuraEnabled(cacheable=true)
    public static List<PricebookEntry> getProducts(String searchTerm) {
        return [
            SELECT Id, UnitPrice, Product2.Name
            FROM PricebookEntry
            WHERE Product2.Name LIKE :('%' + searchTerm + '%')
            AND IsActive = TRUE
            LIMIT 50
        ];
    }
}

🧩 Component HTML: productTable.html

<template>
    <lightning-card title="Product List">
        <lightning-input 
            type="search" 
            label="Search" 
            value={searchTerm} 
            onchange={handleSearch}>
        </lightning-input>

        <template if:true={isTableLoading}>
            <div class="slds-m-vertical_medium slds-align_absolute-center">
                <lightning-spinner alternative-text="Loading..." size="small"></lightning-spinner>
            </div>
        </template>

        <lightning-datatable
            key-field="Id"
            data={filteredProducts}
            columns={columns}>
        </lightning-datatable>
    </lightning-card>
</template>

🧠 Component JS: productTable.js

import { LightningElement, track } from 'lwc';
import getProducts from '@salesforce/apex/ProductService.getProducts';

export default class ProductTable extends LightningElement {
    @track products = [];
    @track searchTerm = '';
    @track isTableLoading = false;
    debounceTimer;

    columns = [
        { label: 'Name', fieldName: 'ProductName' },
        { label: 'Price', fieldName: 'UnitPrice', type: 'currency' }
    ];

    connectedCallback() {
        this.loadInitialProducts();
    }

    loadInitialProducts() {
        this.isTableLoading = true;
        getProducts({ searchTerm: '' })
            .then(result => {
                this.products = result.map(entry => ({
                    Id: entry.Id,
                    ProductName: entry.Product2.Name,
                    UnitPrice: entry.UnitPrice
                }));
            })
            .finally(() => {
                this.isTableLoading = false;
            });
    }

    handleSearch(event) {
        this.searchTerm = event.target.value;
        clearTimeout(this.debounceTimer);
        this.debounceTimer = setTimeout(() => {
            this.fetchFilteredProducts();
        }, 400);
    }

    fetchFilteredProducts() {
        this.isTableLoading = true;
        getProducts({ searchTerm: this.searchTerm })
            .then(result => {
                this.products = result.map(entry => ({
                    Id: entry.Id,
                    ProductName: entry.Product2.Name,
                    UnitPrice: entry.UnitPrice
                }));
            })
            .finally(() => {
                this.isTableLoading = false;
            });
    }

    get filteredProducts() {
        return this.products;
    }
}

✅ Result

  • Typing in the search input shows a small spinner inside the table
  • The input stays focused — no full UI reload
  • Performance is smoother and users stay happy

That’s it — a clean and elegant solution for live filtering a datatable in LWC without disrupting the entire component.


📁 View the Full Working Project on GitHub

You can browse or clone the complete working example on GitHub:

👉 https://github.com/sivanirina/lwc_table_reload_demo

Happy coding! 🚀

Comments

Popular Posts