-2

In one angular component, I am trying to put progress spinner over data loading. User click on html button and below function executes. Array filter may take some time depending upon array size. I want variable isLoading to be true in the beginning and false when filter function is completed. Variable isLoading is used to show/hide progress spinner.

myfunction(val){
    this.isLoading = true   
    var data =  this.myarray.filter(function(item) { 
      // this.isLoading = false // tried this, throws error - Cannot set property 'isLoading' of undefined     
      return item.name == val;      
    });
    this.showdata = data[0]
    // this.isLoading = false // tried this, but this does not show effect
}

Please suggest, how to handle this. Thanks

usersam
  • 761
  • 3
  • 17
  • 37
  • and `this` inside `(function(item) {` will be this of the anonymous function. use lambda function to use outside/parent this. – xdeepakv May 17 '20 at 05:58
  • "_tried this, but this does not show effect_" Can you create a [small demo](https://stackoverflow.com/help/minimal-reproducible-example) for this using [jsfiddle](https://jsfiddle.net/) or [snippet](https://meta.stackoverflow.com/a/358993/1823841) here to show the issue happening. – palaѕн May 17 '20 at 06:02

3 Answers3

0

Filter is not an asynchronous method, your program will wait till its completion. You simply have to remove the this.isLoading = false from inside the filter:

this.isLoading = true;
var data =  this.myarray.filter(function(item) { 
      return item.name == val;      
    });
this.isLoading = false;

this has a different context inside the function, and is not the same as that object referred to by the outer this

VedantBang
  • 704
  • 4
  • 15
  • I need to make "isLoading" true in beginning. I tried your suggestion but this.isLoading = false;immediately overwrites the value and does not wait for filter to completed. – usersam May 17 '20 at 06:02
  • I have edited the answer, included an assignment statement at the start. I hope that helps – VedantBang May 17 '20 at 06:06
  • my mistake. I should have mentioned that this function is part of one angular component. It unnecessarily created confusion of . edited the question – usersam May 17 '20 at 06:09
0

This is happening because functions in JavaScript has their own context, meaning this inside the function that does the filtering is different than the one inside the myfunction.

To pass the context to another function you can use bind.

myfunction(val){
    this.isLoading = true   
    var data =  this.myarray.filter((function(item) { 
       this.isLoading = false;
      /* ... */
    }).bind(this));
    /* ... */
}

Alternatively if your environment support ES6 or you're using babel you can use arrow functions instead:

myfunction(val){
    this.isLoading = true   
    var data =  this.myarray.filter((item) => { 
       this.isLoading = false;
      /* ... */
    });
    /* ... */
}

This will fix the error, but won't achieve your desired behavior because as @VedantBang in his answer mentioned that the filter method is not asynchronous.

What you could do:

myfunction(val){
    this.isLoading = true;
    setTimeout((function() {
      var data =  this.myarray.filter(function(item) { 
         return item.name == val;      
      });
      this.showdata = data[0]
      this.isLoading = false;
    }).bind(this), 1000 /* Simulate 1000 milliseconds delay */);
}
Omar Mneimneh
  • 426
  • 5
  • 13
0

You can use async-await for this

async myfunction(val){
    this.isLoading = true   
    var data = await getFilteredData();
    this.showdata = data[0]
    this.isLoading = false
}

getfilteredData(){
 return new Promise(function(resolve, reject) {     
      resolve(this.myarray.filter((item) item.name === val));      
    });
  }

Now when the filtered data will be returned only then the isLoading will be set to false

Shlok Nangia
  • 2,191
  • 3
  • 12
  • 23