inovua / reactdatagrid

Empower Your Data with the best React Data Grid there is
https://reactdatagrid.io
Other
3.44k stars 57 forks source link

Error getting filtered rows with custom editor #311

Closed fly-robin-fly closed 1 year ago

fly-robin-fly commented 1 year ago

I need to get all filtered rows. As mentioned in issue #190 this should work since version 4.2.0

I do have some custom filter editors. Example of one custom filter editor:

defaultFilterValue:

{ name: 'details', operator: 'hasDetail', type: 'detailsSelector', value: null, emptyValue: null }

filterTypes:

detailsSelector: {
    name: 'detailsSelector',
    operators: [
      {
        name: 'hasDetail',
        fn: ({ value, filterValue, data }) => {
          if (filterValue == null) return true;
          if (filterValue.includes('valuemapping')) {
            if (data.valuemapping === null) return false;
          }
          if (filterValue.includes('user_fields')) {
            if (Object.keys(data.user_fields).length === 0) return false;
          }
          return true;
        },
      },
    ],
  },

Everything is working fine. To get all filtered rows, I found following code in the examples:

  const onFilterValueChange = useCallback(
    (filterValue) => {
      const data: any = filter(props.datapoints, filterValue);
      console.log(data);
    },
    []
  );

Every time I’m changing a filter value, it throws the following exception:

filter.js:102 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'hasDetail')
    at validateFilters (filter.js:102:1)
    at doFilter (filter.js:141:1)
    at filterFn (filter.js:160:1)
    at Array.filter (<anonymous>)
    at filter (filter.js:168:1)
    at DatapointTable.tsx:269:1
    at setter (useProperty.js:58:1)
    at index.js:339:1
    at batchUpdate.js:37:1
    at Array.forEach (<anonymous>)
inovua-admin commented 1 year ago

You have to pass filterTypes object as an extension of ReactDataGrid.defaultProps.filterTypes, as follows:

const filterTypes = Object.assign(ReactDataGrid.defaultProps.filterTypes, {
  detailsSelector: {
    name: 'detailsSelector',
    operators: [
      {
        name: 'hasDetail',
        fn: ({ value, filterValue, data }) => {
          if (filterValue == null) return true;
          if (filterValue.includes('valuemapping')) {
            if (data.valuemapping === null) return false;
          }
          if (filterValue.includes('user_fields')) {
            if (Object.keys(data.user_fields).length === 0) return false;
          }
          return true;
        },
      },
    ],
  },
})

const App = () => {
  const initialData = useCallback(() => filter(people, defaultFilterValue), []);

  const [dataSource, setDataSource] = useState(initialData);
  const [filterValue, setFilterValue] = useState(defaultFilterValue);

  const onFilterValueChange = useCallback(filterValue => {
    const data = filter(people, filterValue);

    setFilterValue(filterValue);
    setDataSource(data);
  }, []);

  return (
    <div>
      <h3>
        Filterable DataGrid with column.filterEditor - Custom checkbox filter
      </h3>

      <ReactDataGrid
        idProperty="id"
        style={gridStyle}
        filterTypes={filterTypes}
        columns={columns}
        dataSource={dataSource}
        filterValue={filterValue}
        onFilterValueChange={onFilterValueChange}
      />
    </div>
  );
};
fly-robin-fly commented 1 year ago

This does sadly not change anything. I copied your code from your website and added some lines of your example above:

import React, { useCallback, useState } from 'react';

import ReactDataGrid from '@inovua/reactdatagrid-enterprise';
import '@inovua/reactdatagrid-enterprise/index.css';

import NumberFilter from '@inovua/reactdatagrid-community/NumberFilter';
import BoolFilter from '@inovua/reactdatagrid-community/BoolFilter';

import people from './people';
import flags from './flags';
import filter from '@inovua/reactdatagrid-community/filter';

const gridStyle = { minHeight: 600 };

const filterTypes = Object.assign({}, ReactDataGrid.defaultProps.filterTypes, {
  country: {
    name: 'country',
    operators: [
      {
        name: 'europe',
        //@ts-ignore
        fn: ({ value, filterValue, data }) => {
          if (filterValue == null) {
            return true;
          }
          const isInEurope = value !== 'usa' && value !== 'ca';

          return filterValue ? isInEurope : !isInEurope;
        },
      },
    ],
  },
});

const defaultFilterValue = [
  { name: 'country', operator: 'europe', type: 'country', value: true },
  { name: 'city', operator: 'startsWith', type: 'string', value: '' },
  { name: 'name', operator: 'startsWith', type: 'string', value: '' },
  { name: 'age', operator: 'gte', type: 'number', value: 21 },
];

const columns = [
  { name: 'id', header: 'Id', defaultVisible: false, defaultWidth: 50, type: 'number' },
  { name: 'name', header: 'Name', defaultFlex: 1, maxWidth: 200 },
  {
    name: 'country',
    header: 'Country - with custom checkbox',
    defaultFlex: 1,
    minWidth: 300,
    filterEditor: BoolFilter,
    //@ts-ignore
    render: ({ value }) => (flags[value] ? flags[value] : value),
  },
  { name: 'city', header: 'City', defaultFlex: 1 },
  { name: 'age', header: 'Age', defaultFlex: 1, type: 'number', filterEditor: NumberFilter },
];

const Test = () => {
  const initialData = useCallback(() => filter(people, defaultFilterValue), []);

  const [dataSource, setDataSource] = useState<any>(initialData);
  const [filterValue, setFilterValue] = useState(defaultFilterValue);

  const onFilterValueChange = useCallback((filterValue) => {
    const data = filter(people, filterValue);

    setFilterValue(filterValue);
    setDataSource(data);
  }, []);

  return (
    <div>
      <h3>Filterable DataGrid with column.filterEditor - Custom checkbox filter</h3>
      <div style={{ marginBottom: 20 }}></div>
      <ReactDataGrid 
      idProperty="id" 
      style={gridStyle} 
      filterTypes={filterTypes} 
      columns={columns} 
      dataSource={dataSource} 
      filterValue={filterValue} 
      onFilterValueChange={onFilterValueChange} />
    </div>
  );
};

export default () => <Test />;

Getting following error:

react-dom.development.js:11392 Uncaught TypeError: Cannot read properties of undefined (reading 'emptyValue')
    at validateFilters (filter.js:101:1)
    at doFilter (filter.js:141:1)
    at filterFn (filter.js:160:1)
    at Array.filter (<anonymous>)
    at filter (filter.js:168:1)
    at Test.tsx:59:1
    at mountState (react-dom.development.js:15594:1)
    at Object.useState (react-dom.development.js:16203:1)
    at useState (react.development.js:1469:1)
    at Test (Test.tsx:61:1)