[SalesForce] Searchable & Selectable Lightning Datatable to keep selections between searches

I am creating a custom Lightning Component to quickly add products to a quote. The Datatable pulls back all active products, I have a search field that the user can use to filter the products, when I search for Product "ABC", select it, and then search for product "DEF", then select it, I want to be able to add those rows to a new datatable with both products. Currently when I filter the data table it only finds the most recent selected rows.

If I relate it to something I did in my old vb.Net days it would be the "+=" operator, essentially I want to build a list that persists over the users search results.

Here is what I have:

Apex Controller:

public class ProductQuickAddController {
@AuraEnabled
public static List<Product2> getProducts() {
    
 List<Product2> activeProducts = [SELECT Id, Name, ProductCode, Family, Description FROM Product2 where IsActive = True];
    
 //system.debug(activeProducts);  
    
 return activeProducts;
    
}
@AuraEnabled
public static List<Product2> getResults(string searchStr) {
    
 searchStr = '%' +searchStr + '%';   
    
 system.debug(searchStr);   
    
 List<Product2> activeProducts = [SELECT Id, Name, ProductCode, Family, Description FROM Product2 where (Name like :searchStr or Description like :searchStr) and IsActive = True];
    
 //system.debug(activeProducts);  
    
 return activeProducts;
    
}

}

Component:

<aura:component Controller = "ProductQuickAddController" implements="force:hasRecordId,flexipage:availableForAllPageTypes,force:appHostable,force:lightningQuickAction" access="global">
<!-- attributes -->
<aura:attribute name="productList" type="Object" />
<aura:attribute name="productCols" type="List" />
<aura:attribute name="filteredProducts" type="List" />
<aura:attribute name="selectedRowsCount" type="Integer" default="0"/>
<aura:attribute name="selectedRows" type="List"/>

<aura:attribute name="searchText" type="String" />
<ltng:require styles="{!$Resource.ModalCSS}"/>

<aura:attribute name="addProductsCols" type="List" />

<aura:attribute name="errors" type="Object" default="[]"/>
<aura:attribute name="draftValues" type="Object" default="[]"/>

<!-- handlers-->


<aura:handler name="init" value="{!this}" action="{!c.doInit}" />


<div style="height: 90%; padding: 10px;" aura:id="Products">
    <lightning:layout verticalAlign="end">
<lightning:layoutItem flexibility="grow">
    <lightning:input label="Search" type="search" value="{!v.searchText}" name="search"/>
</lightning:layoutItem>
<lightning:layoutItem class="slds-p-left_x-small">
    <lightning:button variant="brand" label="Search" title="Search" onclick="{!c.filter }" />
</lightning:layoutItem>
</lightning:layout>
    
  
     <h1>Selected Rows: {!v.selectedRowsCount }</h1>
    <center>
        <div style="height: 500px; padding: 10px;">
    <lightning:datatable
        aura:id="tblProducts"
        columns="{!v.productCols}"
        data="{!v.filteredProducts}"           
        keyField="Id"
        onrowselection="{!c.getSelected}"/>
    </div>
        </center> 
    <div id="controls" style="float:right; padding: 10px;">
        <lightning:button variant="brand" label="Set Prices" title="Set Prices" onclick="{!c.selectedProducts }" />
</div>
</div>

<div style="height: 500px; overflow: scroll; padding: 10px;" aura:id="addProducts">
<lightning:datatable
        aura:id="SelectedProducts"
        columns="{!v.addProductsCols}"
        data="{!v.selectedRows}"           
        keyField="Id"
        errors="{!v.errors}"
        draftValues="{!v.draftValues}"
        onsave="{!c.handleSaveEdition}"/>
        
       
</div>


</aura:component>

Controller:

({
doInit : function(component, event, helper) {
    
    component.set('v.productCols', [
        //{label: 'Id', fieldName: 'Id', type: 'text'},
        {label: 'Name', fieldName: 'Name', type: 'text'},
        {label: 'Code', fieldName: 'ProductCode', type: 'text'},
        {label: 'Family', fieldName: 'Family', type: 'text'},
        {label: 'Description', fieldName: 'Description', type: 'text'}
    
    ]);
    
    component.set('v.addProductsCols', [
        //{label: 'Id', fieldName: 'Id', type: 'text'},
        {label: 'Name', fieldName: 'Name', type: 'text'},
        {label: 'Description', fieldName: 'Description', type: 'text', editable: true}, 
        {label: 'Sales Price', fieldName: 'Description', type: 'currency', typeAttributes: { currencyCode: 'USD'}, editable: true, typeAttributes: { required: true }}, 
        {label: 'Quantity', fieldName: 'Description', type: 'number', editable: true, typeAttributes: { required: true }}
    
    ]);

    helper.fetchProducts(component, event);
    
     $A.util.removeClass(component.find("Products"), "slds-hide");
    $A.util.addClass(component.find("addProducts"), "slds-hide");
    
}, 
filter: function(component, event, helper) {
    
 
    helper.getSearchResults(component, event);
}, 
getSelected: function(component, event, helper) {
    
    var prevSelected = component.get("v.selectedRows");
    
    let selectedProds =[];
    
    selectedProds = component.find('tblProducts').getSelectedRows();
    

    component.set("v.selectedRows", selectedProds)
    
    
},

selectedProducts: function(component, event, helper) {
    
    $A.util.removeClass(component.find("addProducts"), "slds-hide");
    $A.util.addClass(component.find("Products"), "slds-hide");        
   
    
}, 


})

Helper:

({
fetchProducts : function(component, event) {
    
      var action = component.get('c.getProducts')
    
      var self = this;
           
             
      action.setCallback(this, function(response) {
        component.set('v.productList', response.getReturnValue());
        component.set('v.filteredProducts', response.getReturnValue());
    } );
    $A.enqueueAction(action);
    
}, 
getSearchResults : function(component, event) {
 
    var strSearch = component.get("v.searchText")
    
    var action = component.get('c.getResults')
    
    var self = this;
      action.setParams({"searchStr":strSearch});
             
      action.setCallback(this, function(response) {
        component.set('v.productList', response.getReturnValue());
        component.set('v.filteredProducts', response.getReturnValue());            
    } );
    $A.enqueueAction(action);
    
    
}
})

Best Answer

So the fix was adding the following to the Component side:

<aura:attribute name="selection" type="List" />

<lightning:datatable
            aura:id="tblProducts"
            columns="{!v.productCols}"
            data="{!v.filteredProducts}"           
            keyField="Id"
            selectedRows = "{!v.selection}"
            onrowselection="{!c.getSelected}"/>

<lightning:datatable
            aura:id="SelectedProducts"
            columns="{!v.addProductsCols}"
            data="{!v.selection}"           
            keyField="Id"
            hideCheckboxColumn="true"            
            draftValues="{!v.draftValues}"
            onsave="{!c.handleSave}"/>

And in the controller:

getSelected: function(component, event, helper) {
                
        var selectedRows = event.getParam('selectedRows');  
        var allSelectedRows = component.get("v.selection");
        
        selectedRows.forEach(function(row) {
                allSelectedRows.push(row);
            });
        
        component.set("v.selection", allSelectedRows);
    
    }
Related Topic