10

Am using custom search filter

HtML

 <input type="text" pInputText class="ui-widget ui-text" [(ngModel)]
 ="gloablFilterValue" (ngModelChange)="splitCustomFilter()" placeholder="Find" />

I am using ngModelChange() event on search box


globalSearch(realData, searchText, columns) {
        searchText = searchText.toLowerCase();
        return realData.filter(function (o) {
            return columns.some(function (k) {
                return o[k].toString().toLowerCase().indexOf(searchText) !== -1;
            });
        });
    }

    splitCustomFilter() {
    let columns =  
   ['PartNoCompleteWheel', 'DescriptionCompleteWheel', 'PartNoTyre', 'DescriptionTyre', 'PartNoRim', 'DescriptionRim','DeletedDateFromKDPStr', 'DateFromKDPStr', 'Status'];
   this.tyreAndRimList = this.globalSearch(this.tyreAndRimList, this.gloablFilterValue, columns); 
    }

The this.tyreAndRimList list of values for the columns which is mentioned in a column variable.

Problem

The filter is working good! But the main problem is filter performance is very poor while the record count is huge(more than 100 rows per every column)

When

The filter is working good if am entering a single character (like a). But when I was typing the character continuously the browser is hanging. the reason is the filter has been firing every typing on the filter box(because of am using ngModelChange()// onchange() event)

What I want

I want to stop filtering if the user typing continuously on the search box. Once the user has stop the typing then only I need to start filtering.

What I did

I have tried to handle this by using setTimeout(). But it just wait the filter call for a second. It is working if the user entered just 2 or 3 character's continuously. But if the user typing more than 7 or 8 or above character's, it continues to hang the browser. because of many filter callbacks are processing on the same time.

 setTimeout(() => //code of filtering ,1000);

Question

How to stop filtering while user continuously entering value and start the filtering once the user has been stop the typing?

I am working in angular-2 and typescript. But this question is not related with only for angularjs or angular or JavaScript or typescript because of I want an idea not a solution. So I'll add those all tags for this question. Don't remove it. Thanks

Ramesh Rajendran
  • 37,412
  • 45
  • 153
  • 234
  • 9
    You can't stop a `filter` before it's done. You can, on the other hand, use a `for` loop that you can `break`. Other possibility : [debounce your filter](https://stackoverflow.com/questions/41935424/how-to-achieve-a-debounce-service-on-input-keyup-event-in-angular2-with-rxjs) – Jeremy Thille Dec 11 '17 at 15:09
  • 5
    you need to [debounce](https://davidwalsh.name/javascript-debounce-function) the event. – Aᴍɪʀ Dec 11 '17 at 15:10
  • So look at debounce and also if filtered, there is no need to filter the entire list when they typed one more character, just filter the filtered set. – epascarello Dec 11 '17 at 15:11
  • Of course debounce, but on top of that you could probably optimise the code some more. For example you could(assuming the text is static) make a single concatenated string during init of all the cells per row. And then only check the concatenated string per row. That way you can already skip looping all columns+strtolower in your filter. – René Dec 11 '17 at 15:21
  • 1
    Try to take a look at https://stackoverflow.com/a/36849347/356380 – Whisher Dec 11 '17 at 15:26
  • @Whisher Nice! I'll take a look at that. and try that. – Ramesh Rajendran Dec 11 '17 at 15:28
  • If this process is a core feature, and debouncing isn't enough, consider delegating the computation to a WebWorker so it doesn't tie up your main thread.. – zero298 Dec 11 '17 at 16:02
  • Possible duplicate of [How to achieve a debounce service on input keyup event in angular2 with rxjs](https://stackoverflow.com/questions/41935424/how-to-achieve-a-debounce-service-on-input-keyup-event-in-angular2-with-rxjs) – Owen Pauling Dec 11 '17 at 16:16

4 Answers4

2

Debounce the function. See how underscore does it here: Function Debouncing with Underscore.js.

You would then generate a debounced version of your function like this:

var globalSearchDebounced = _.debounce(globalSearch, 100, false);

It will only call after the user has stopped typing for at least one second.

Faust
  • 15,130
  • 9
  • 54
  • 111
  • An alternative to this solution would be making the form reactive, subscribing to the input's value changes, then applying the rxjs debounce operator. Could also add additional operators such as filter to enforce a minimum number of characters before executing the array filter. – Harry Dec 11 '17 at 15:24
  • You can't "just copy 14 lines". There's still a license with terms that have to be followed when you use part of the code. – Ingo Bürk Dec 11 '17 at 19:32
  • @IngoBürk true enough. The offending sentence is removed. – Faust Dec 11 '17 at 21:43
1

It's not possible to interrupt the Array.filter method. Based on what you need you could handle this like this:

let timerId = null

function handleChange() {
  if(timerId) {
    clearTimeout(timerId)
  }

  timerId = setTimeout(() => /* Array.filter(...) */, 1000)
}

Explanation

Have a variable which will contain the timerId returned from the setTimeout function. Every time the model get changed the handleChange function will be called (in this example). The function checks if the variable which contains the timerId is set and contains a timerId, when the variable contains the timerId the clearTimeout function will be called to clear the previous timeout after that the handleChange creates a new timeout and assigns the timerId (returned from the setTimeout function) to the variable.

Siggy
  • 324
  • 2
  • 11
1

Without underscore, and without a Timeout (that will trigger the whole Angular lifecycle by the way), you can use an Observable with the async pipe and a debounce.

In your global search function :

return Observable.of(/* filter here and return the filtered array */).debounceTime(1000)

In your list (that has to be somewhere I guess)

<list-item *ngFor="let x of myFilteredResults | async">...</list-item>
0

I have complete it by using Subject to debounceTime.

private subject = new Subject<string>()
ngOnInit() {
        this.subject.debounceTime(300).subscribe(inputText => {
        this.gloablFilterValue = inputText;
        this.splitCustomFilter();   // filter method  
        });    
    }

Now when I change the value in this.gloablFilterValue object by using change event. It just waiting for the end of event completion.

Ramesh Rajendran
  • 37,412
  • 45
  • 153
  • 234