2

I am trying to create a search box that is supposed to do the following when the input value changes or if the user clicks the search icon.

  • Set the value of searchText field. This is used for searching and also, controls which icon (search | clear) to show next to the search input box.

  • Before searching, the value of the field previewTableLoading is set to true which is used in the template to display a spinner.

  • After a time-consuming search process, the value of the field previewTableLoading is set back to false to stop the spinner.

Here's the template for the search box -

<div class="ui icon input">
    <input type="text" (change)="onSearchFieldChange($event)" placeholder="Search Fields..." #searchInput>
    <i class="search link icon" (click)="searchInput.change()" *ngIf="!searchText || searchText === ''"></i>
    <i class="times link icon" (click)="clearSearch()" *ngIf="searchText && searchText !== ''"></i>
</div>

And here is the implementation code -

onSearchFieldChange(event) {
    this.searchText = event.target.value;
    this.search();
}

search() {
    this.previewTableLoading = true;

    // SOME HEAVY SEARCHING ALGORITHM THAT TAKES TIME

    this.previewTableLoading = false;
}

clearSearch() {
    this.searchText = '';
    this.search();
}

PROBLEM:

When the input value changes, the value of the field previewTableLoading isn't updated instantaneously. It waits for the entire search method to complete. That is why my spinner which is bound to that field never shows up. I feel like the method call from the template is acting synchronously and all the updates within that method are only coming into effect when the method completes.

I hope I was clear explaining my issue. It'd be great if anyone can help me with understanding the issue. Thanks.

yuva
  • 3,108
  • 4
  • 20
  • 35

2 Answers2

3

This would be a great time to use an Observable.

I'm not sure how you are doing your search but lets say you are calling some service that you have which returns an observable (i.e. a http request)

onSearchChange(event) {
   this.search(event.target.value);
}

search(searchTerm) {
    this.previewTableLoading = true;
    this.searchService.search(searchTerm)
    .subscribe( (results) => {
        //this is the result set that we get back
        this.resultSet = results;
        this.previewTableLoading = false;
    });
}
spfellers
  • 141
  • 5
1

Your changes will not reflect on UI while your script is running. As a simple test, have an infinite loop and update background color of an element before entering the loop. You will not see the changes. It wouldn’t reflect the changes until function done execution. A solution would be

search() {
    this.previewTableLoading = true;
    setTimeout( () => { // Causes Angular change detector to kick in 
        // SOME HEAVY SEARCHING ALGORITHM THAT TAKES TIME

        this.previewTableLoading = false;
        }, 0);
}

This will let the changes reflect before searching. Also, simply setting the previewTableLoading to true inside a setTimeout works as well.

yuva
  • 3,108
  • 4
  • 20
  • 35
printfmyname
  • 983
  • 15
  • 30
  • That worked like a charm. Thank You. However, is it a good practice? I am using ngRX with my project, do you think dispatching an action and performing those heavy search operation in Effect or a Service would be a better approach? So many questions :) – yuva Aug 17 '18 at 22:22
  • 1
    @yuva i don't think using setTimeout is encouraged very much. you can look in to NgZone https://blog.thoughtram.io/angular/2016/01/22/understanding-zones.html which does a similar thing to setTimeout – printfmyname Aug 17 '18 at 22:45
  • 1
    An alternative is to call `ChangeDetectorRef.detectChanges`. See [this post](https://stackoverflow.com/q/34827334/1009922). – ConnorsFan Aug 17 '18 at 22:48