Search code examples
javascriptrreactable

Table disappearing if inputting special characters


I am using reactable with custom filtering/searching methods and regular expression patterns, and special functions to collapse rows if they are identical (to avoid repetition of the same rows, which can occur in my database). See a reproducible example below:

library(reactable)
library(htmltools)

customsearch <-  JS('
function(rows, columnIds, filterValue) {
    let arrFilterValues = filterValue.split(" ");

    return rows.filter(function(row) {
        return arrFilterValues.map(function(e) {
            return columnIds.some(function(columnId) {
                return new RegExp(e,"i").test(row.values[columnId])
            });
        }).every(v => v === true);
    });
}')

customfilter <- JS('function(rows, columnId, filterValue) {
    let arrFilterValues = filterValue.split(" ");

    return rows.filter(function(row) {
        return arrFilterValues.map(function(e) {
                return new RegExp(e,"i").test(row.values[columnId])
        }).every(v => v === true);
    });
}')


reactable(iris,filterable=TRUE,searchable=TRUE,static=TRUE, rowStyle=JS("function(rowInfo, state) {
    const firstSorted = state.sorted[0]
    if (!firstSorted || firstSorted.id === 'Petal.Length') {
      const prevRow = state.pageRows[rowInfo.viewIndex - 1]
      if (prevRow && rowInfo.values['Petal.Length'] === prevRow['Petal.Length']) {
        return { visibility: 'collapse' }
      }
    }
  }"), searchMethod = customsearch, defaultColDef=colDef(filterMethod=customfilter, html=TRUE))

The problem is that, if I input some characters in the search box, the table disappear and the only way to make it appear again is to refresh the page: I can put numbers or text in the search box, but as soon as I input "\" (as well as other special characters I guess, such as "*") the table disappears. This also occur if I input a string which is not found in the table.

Is there a way to prevent this behavior?


Solution

  • The table disappears if the filtered result set has zero rows because in the rowStyle function you have a rowInfo.viewIndex and this is undefined if the filtered table has zero rows (in particular, rowInfo is undefined in this case). I added an extra condition inside the function which checks for this case, this should avoid the crash.

    Concerning the crash if the user inputs a special character the problem is that that in this case no regex for the search can be constructed. Below I implemented a solution where in such a situation I remove the last character from the filter value and return this result set (i.e., the last filtered result set is kept). There are also other possibilities, e.g. returning an empty table.

    library(reactable)
    library(htmltools)
    
    customsearch <-  JS('
    function(rows, columnIds, filterValue) {
        let arrFilterValues = filterValue.split(" ");
          return rows.filter(function(row) {
              return arrFilterValues.map(function(e) {
                  return columnIds.some(function(columnId) {
                      try {
                          return new RegExp(e,"i").test(row.values[columnId]);
                      } catch (err) {
                          return new RegExp(e.slice(0, -1), "i").test(row.values[columnId]);;
                      }
                  });
              }).every(v => v === true);
          });
    }')
    
    customfilter <- JS('function(rows, columnId, filterValue) {
        let arrFilterValues = filterValue.split(" ");
    
        return rows.filter(function(row) {
            return arrFilterValues.map(function(e) {
                try {
                    return new RegExp(e,"i").test(row.values[columnId]);
                } catch (err) {
                    return new RegExp(e.slice(0, -1), "i").test(row.values[columnId]);;
                }
            }).every(v => v === true);
        });
    }')
    
    reactable(iris,filterable=TRUE,searchable=TRUE,static=TRUE, rowStyle=JS("function(rowInfo, state) {
        const firstSorted = state.sorted[0]
        if ((!firstSorted || firstSorted.id === 'Petal.Length') && (rowInfo !== undefined)) {
          const prevRow = state.pageRows[rowInfo.viewIndex - 1]
          if (prevRow && rowInfo.values['Petal.Length'] === prevRow['Petal.Length']) {
            return { visibility: 'collapse' }
          }
        }
      }"), searchMethod = customsearch, defaultColDef=colDef(filterMethod=customfilter, html=TRUE))