[SalesForce] Nested tables with different headers and data LWC for:each

I have a requirement to create a component that is essentially 2 tables, parent/child. The parent table is Subscriptions, and the child table is Subscription Products that are related to that subscription. both tables have different headers, the parent table has radio buttons, and the child has checkboxes. the user selects one of the specific subscriptions, the child table displays, then user checks the child products they want to act on.

Right now I am pulling the data in a single query/backend method, and building one of the objects for displaying the parent table on the front end.

my problem is two fold but mostly layout wise. I have created the relationship between subscription and products, but when I try nesting both tables with a for:each directive I can't seem to align them, the child table goes into the first column of the parent table. It also renders under each subscription row, and I only want it to render on the selected parent row it is related to.

I have also tried with lightning-layout, which seems like it will get me the same visuals as a table but I'm still struggling a bit with the nesting. I know I can also make some rows a child component, but the issue is still i need to conditionally render within each specific row, so its really a 3rd layer of conditional rendering almost.

I can share code but I guess this is more of an architecture question. I have "subscriptions" and "subscription products" in an object, so they are available to easily iterate over, and I have the event handling built for each table. So really just looking for if this is possible and suggestions on the approach for the conditional rendering?

enter image description here

Best Answer

Here's what worked for me. I'm pretty green so there's probably a cleaner way, I created a child component with the products:

<template>
    <table>
        <thead>
            <th></th>
            <th>Product Name</th>
            <th>Start Date</th>
            <th>Term Length</th>
            <th>Seats</th>
            <th>Expiring</th>
        </thead>
        <tbody>
        <template for:each={selectedSubProds} for:item="product">
            <tr key={product.srpId}> 
                <td class="checkbox-cell">
                <input type="checkbox" data-key={product.srpId} name="selectedproduct" value={product.srpId} checked={checked} onchange={handleProdChange}/></td>
                <td>{product.name}</td>
                <td><lightning-formatted-date-time value={product.startDate} year="numeric" month="numeric" day="numeric" ></lightning-formatted-date-time></td>
                <td>{product.termLength}</td>
                <td>{product.seats}</td>
                <td><lightning-formatted-date-time value={product.expirationDate} year="numeric" month="numeric" day="numeric"></lightning-formatted-date-time></td>
            </tr>
        </template>
        </tbody>
    </table>

then I put it in the parent

<template for:each={subscriptionsTable} for:item="sub">
    <tr key={sub.id} data-key={sub.id} data-name="subs">
        <td class="radio-cell">
            <input type="radio" data-key={sub.id} name="subscription" value={sub.id} onchange={handleSubChange}/>
        </td>
        <td colspan="2">{sub.name}</td>
        <td><lightning-formatted-date-time value={sub.customerAcceptanceDate} year="numeric" month="numeric" day="numeric" time-zone="UTC"></lightning-formatted-date-time></td>
        <td colspan="4">{sub.mrr}</td>
    </tr>
    <tr data-name="child-table" data-key={sub.id} key={sub.id}>
        <td colspan="5">
            <c-subscription-products
                data-active-key={sub.id} 
                key={sub.id}
                onselected={handleProdChange}
            >
            </c-subscription-products>
        </td>
    /tr>   
</template>      

then i called the function to select the child products from the parent to the child component, matching the rowId, and also edited display properties in a foreach loop

handleSubChange(event) {
    //handle subscription level row selection
    this.selectedSubscription = event.target.dataset.key;
     
    const childRows = this.template.querySelectorAll('tr[data-name="child-table"]');
    console.log(childRows);
    console.log(this.selectedSubscription);
        childRows.forEach(row => {
            console.log(row.dataset.key);
        if(row.dataset.key === this.selectedSubscription) {
            row.style.display = "table-row";
        }
        else {
            row.style.display = "none";
        }
    }); 

    this.template.querySelector("c-subscription-products[data-active-key='" + this.selectedSubscription + "']").handleProductSelect(this.productsTable, this.selectedSubscription);

}
Related Topic