[SalesForce] Infinite Scrolling in lightning:datatable combined with search filter functionality

I got a lightning:datatable where I also have a search input field to search for whatever the user types in the input field. I can't get this to work together with an infinite scrolling table. Wonder if someone can help me out. If I have infinitescrolling enabled, the filter functionality does not work (I assume because the table keeps re-rendering when I type into the input field)

My markup:

<extends="c:Base" controller="AccountBrowserForm">
<aura:handler name="init" value="{! this }" action="{! c.doInit }"/>
<aura:attribute name="selectedAccountId" type="Id" access="public" />
<aura:attribute name="accounts" type="List"/>
<aura:attribute name="mycolumns" type="List"/>
<aura:attribute name="sortedBy" type="String" />
<aura:attribute name="sortedDirection" type="Boolean" default="true" />
<aura:attribute name="backingdata" type="List" /> <!-- backing data to allow a narrowed down datatable -->
<aura:attribute name="filter" type="String" /> <!-- filter input -->
<aura:registerEvent name="AccountFilterChange" type="c:AccountFilterChange" />



<aura:attribute name="enableInfiniteLoading" type="Boolean" default="true"/>
<aura:attribute name="initialRows" type="Integer" default="5"/>
<aura:attribute name="rowsToLoad" type="Integer" default="1"/>
<aura:attribute name="totalNumberOfRows" type="Integer" default="5"/>
<aura:attribute name="loadMoreStatus" type="String" default=""/>
<aura:attribute name="rowNumberOffset" type="Integer" default="0"/>
<aura:attribute name="currentCount" type="Integer" default="10"/>

<div class="forminput">
<lightning:input type="text"
                 onchange="{!c.filter}"
                 value="{!v.filter}"
                 placeholder="{!$Label.c.Searchfield_Placeholder}" />
</div>
<lightning:datatable aura:id="lightningTable"
                     data="{! v.accounts }"
                     columns="{! v.mycolumns }"
                     keyField="Id"
                     onrowaction="{! c.handleRowAction }"
                     onsort="{!c.updateColumnSorting}"
                     sortedBy="{!v.sortedBy}"
                     sortedDirection="{!v.sortedDirection}"

                     loadMoreOffset="{! v.loadMoreOffset }"
                     enableInfiniteLoading="{!v.enableInfiniteLoading}"
                     onloadmore="{! c.loadMoreData }"

                     hideCheckboxColumn="true"/>
{! v.loadMoreStatus }

Controller:

doInit: function (component, event, helper) {
    helper.callServer(component, "c.getAccounts", $A.getCallback(function (response) {
        component.set("v.accounts", response);
        component.set("v.backingdata", response); // Need to fetch a copy because we use this when we filter the data.
        component.set('v.mycolumns', [{
                type: "button",
                typeAttributes: {
                    label: '',
                    iconName: 'utility:add',
                    name: 'selectRecord',
                    title: 'selectRecord',
                    disabled: false,
                    value: 'edit',
                    variant: {
                        fieldName: 'variantValue'
                    },
                }
            },
            {label: $A.get("$Label.c.Name"), fieldName: 'Name', type: 'text', sortable: true},
            {label: $A.get("$Label.c.Street"), fieldName: 'BillingStreet', type: 'text', sortable: true},
            {label: $A.get("$Label.c.Postal_Code"), fieldName: 'BillingPostalCode', type: 'text', sortable: true},
            {label: $A.get("$Label.c.City"), fieldName: 'BillingCity', type: 'text', sortable: true},
            {label: $A.get("$Label.c.Country"), fieldName: 'BillingCountry', type: 'text', sortable: true},
        ]);
    }))
},

filter: function (component, event, helper) {
    var data = component.get("v.backingdata"),
        term = component.get("v.filter"),
        results = data, regex;
    try {
        regex = new RegExp(term, "i");
        // filter checks each row, constructs new array where function returns true
        results = data.filter(row => regex.test(row.Name) ||
        regex.test(row.BillingStreet) ||
        regex.test(row.BillingPostalCode.toString()) ||
        regex.test(row.BillingCity) ||
        regex.test(row.BillingCountry));
    }
    catch (e) {
        // invalid regex, use full list
    }
    component.set("v.accounts", results);
},

loadMoreData: function (cmp, event, helper) {
        //Display a spinner to signal that data is being loaded
        event.getSource().set("v.isLoading", true);
        //Display "Loading" when more data is being loaded
        cmp.set('v.loadMoreStatus', 'Loading');
        helper.fetchData(cmp, cmp.get('v.rowsToLoad'))
            .then($A.getCallback(function (data) {
                if (cmp.get('v.accounts').length >= cmp.get('v.totalNumberOfRows')) {
                    cmp.set('v.enableInfiniteLoading', false);
                    cmp.set('v.loadMoreStatus', 'No more data to load');
                } else {
                    var currentData = cmp.get('v.accounts');
                    //Appends new data to the end of the table
                    var newData = currentData.concat(data);
                    cmp.set('v.accounts', newData);
                    cmp.set('v.loadMoreStatus', '');
                }
               event.getSource().set("v.isLoading", false);
            }));
    }

Helper

fetchData: function(component , rows){
        return new Promise($A.getCallback(function(resolve, reject) {
            var currentDatatemp = component.get('c.getAccounts');
            var counts = component.get("v.currentCount");
            currentDatatemp.setCallback(this, function(a) {
                resolve(a.getReturnValue());
                var countstemps = component.get("v.currentCount");
                countstemps = countstemps+component.get("v.initialRows");
                component.set("v.currentCount",countstemps);
            });
            $A.enqueueAction(currentDatatemp);
        }));

    } ,

Appreciate your help

Best Answer

Hope you have figured this out by now @erikvm :-)

For other viewers, have a look at this very helpful page:

https://rajvakati.com/2018/02/18/usage-of-lightningdatatable/

Also, the fetchData function, which is missing from the 'Documentation' tab, is shown on the 'Example' tab:

lightning:datatable documentation

enter image description here