0
votes

I am building lightweight MS Excel based on React. I am currently building a filtering system that will allow filtering rows based on filter input - each one for each column. It works fine if you filter only through one column.

Problem: If I decide to filter based on more than just 1 column, the newest filtering takes precedence and ignore all previous ones like they weren't there.

I assume I know what the problem is. In current shape, my script always filters data stored in preSearch array which always stores the old original data (const). I tried to perform a loop through all filter inputs but I have failed to join all the conditions using && inside Array.prototype.filter() method.

After clicking the toggle search button this is generated:

showSearchInputs() {
    if (this.state.toggleSearch === true) {
      return (
        <tr onChange={(e) => this._searchData(e)}>
          {
            this.props.headers.map((a, b) => {
              return (
                <td key={b}><input data-idx={b} type="text" style={{fontSize: '11px', width: '150px'}} /></td>
              )
            })

          }
        </tr>
      )
    }
    else {
      return null
    }
  }

And then there is a method that listens for data change:

  _searchData(e) {
    console.log(e.target.dataset.idx, e.target.value);
    if (!e.target.value.toLowerCase()) {
      //restore full old data
      this.setState({
        data: this.preSearch
      })
      return
    }
    else {
      var idx = e.target.dataset.idx 
      var searchdata = this.preSearch.filter(row => {
        return row[idx].toString().toLowerCase().indexOf(e.target.value.toLowerCase()) > -1
      })

      this.setState({
        data: searchdata,
      }) 
    }
  }

Expected result: Filters overlapping (accumulating), currently the newest filter takes precedence Removing characters from the filter inputs also updates the result and aiming to the 0 filters and original data to be displayed (if the user removes all filters naturally)

1

1 Answers

0
votes

Okay, so I have figured it out.

My previous approach was about monitoring changes inside filter rows but only last change was taken into consideration. And that was the problem.

I decided to slightly change method of filtering. I have added extra state property that stores each filter changes and by default I set them to "" as filter method does not work with null or undefined:

this.state = {
    filters: ["", "", "", "", ""]
} 

Now, each time user inputs his filter in specific column, the this.state.filters get updated. At the same time, inside the same method I am also filtering data based on that filter. If all fields are empty, I am restoring original data.

To have multidimension, overlapping filtering I had to ask on SO for a solution as I couldn't combine multiple Booleans in return statement. I get my answer under this post:

How to return booleans joined with && inside callback function in filter method?

Using Array.prototype.every() to check if all items are compliant with callback condition and Array.prototype.includes() to check if they contain element from filters array I managed to do it. Please note, that Array.prototype.includes() is ES6 functionality.