1

我正在尝试做的事情

我有一个要向其提供数据的 DataTable。DataTable 的列是根据提供的数据动态创建的。我想根据列包含的数据类型对每个列应用过滤器。例如,我希望范围过滤器可用于数值数据,并且唯一值的切换下拉列表可用于具有名称列表的列。在这个问题中,为简单起见,我仅以动态方式分配 DropDown 过滤器。

我遇到的问题

我无法以动态方式为每个 Column 组件提供过滤器所需的道具和状态。

我的问题

无法在表格中使用自定义功能组件(请参阅下面的我尝试过的内容):

如何动态创建具有需要自己的状态和处理程序的过滤器的列?在状态正常运行的情况下,我无法找到一种方法来做到这一点。

用例背景

  • 我有一个接受 Columns 作为子项的 DataTable
  • 可以将列配置为使用过滤器(即搜索框,或可以打开和关闭的唯一值的下拉列表)
  • 过滤器需要:
    • 一个 filterElement 函数,它返回过滤器类型的 JSX(IE:如果过滤器是下拉列表,则 filterElement 包含一个 MultiSelect)
      • filterElement 需要
        • 跟踪 selectedOptions 的状态
        • 修改 selectedOptions 的 onChange
          • onChange 需要
            • 对数据表的引用

当我们提前知道列时,以静态方式将下拉过滤器应用于列的示例

export default function DataTableFilterClassTest() {

  const [tableData, setTableData] = useState(data); // Data imported from elsewhere
  const dt = useRef(null);
  const [selectedOptions, setSelectedOptions] = useState(null)

  // Hardcoded some options which are available in the data
  const options = [{ option: 1000 }, { option: 1001 }, { option: 1002 }, { option: 1003 }]

  const onOptionsChange = (e) => {
    // 2nd param 'id' is a reference to the field header this filter applies to
    // Hardcoded for testing purposes
    dt.current.filter(e.value, 'id', 'in')
    setSelectedOptions(e.value)
  }

  const filterElement = () => {
    return (
      <React.Fragment>
        <MultiSelect
          value={selectedOptions}
          options={options}
          onChange={onOptionsChange}
          optionLabel="option"
          optionValue="option"
          className="p-column-filter"
        />
      </React.Fragment>
    )
  }

  return (
    <div className="datatable-filter-demo">
      <div className="card">
        <DataTable ref={dt} value={tableData} paginator rows={10}
          className="p-datatable-customers"
          emptyMessage="No customers found."
        >
          <Column field="id" header="id" filter filterElement={filterElement} />
        </DataTable>
      </div>
    </div>
  );
}

问题

这在我们事先知道列的静态情况下工作得很好,但是当我尝试根据我的数据动态创建列时就会出现问题。我不知道如何为每个动态列创建 filterElement 和 filterElement 所需的状态和 onChange。

// ....

const columnFieldsForMapping = ['id', 'company']

const mappedColumns = columnFieldsForMapping.map((field) => {

  // Need to create a new filterElement
  // filterElement requires its own onChange to modify selected options,
  // and value (a state object, to store the selected options) 

  return (

    <Column key={field} field={field} header={field} filter filterElement={HOW DO I CREATE THIS FOR EACH COLUMN} />

  )
  
})

return (
  <div className="datatable-filter-demo">
    <div className="card">
      <DataTable ref={dt} value={tableData} paginator rows={10}
        className="p-datatable-customers"
        emptyMessage="No customers found."
      >

        {mappedColumns}

      </DataTable>
    </div>
  </div>
);
}

我试过的

创建一个 CustomColumn 组件,扩展 Column 组件

我的第一个解决方案是创建一个 CustomColumn 功能组件,它扩展了 PrimeReact 的列组件。CustomColumn 组件将包含 filterElement 及其所需的状态和功能。它将返回一个带有应用的 filterElement 属性的 Column 组件。不幸的是,我后来了解到 DataTable 不能接受除 Column 组件之外的任何组件。它不会接受扩展原始列组件的反应功能组件。这是 github 问题,指出这是不可能的:https ://github.com/primefaces/primereact/issues/644#issuecomment-790648272

CustomDropDownColumn.js

import React, { useState, useEffect } from 'react'
import { Column } from 'primereact/column';
import { MultiSelect } from 'primereact/multiselect';

export default function CustomDropDownColumn(props) {

  const [state, setState] = useState([])
  
  const onChange = (e) => {
    props.dt.current.filter(e.value, props.field, 'in')
    setState(e.value)
  }
  
  const filterElement = <MultiSelect
    value={state}
    options={props.options}
    onChange={onChange}
    optionLabel="option"
    optionValue="option"
    className="p-column-filter"
  />

  return (

    <Column field={props.field} header={props.field} filter filterElement={filterElement} />
  )
}

使用自定义下拉列:

// ....
return (
  <div className="datatable-filter-demo">
    <div className="card">
      <DataTable ref={dt} value={tableData} paginator rows={10}
        className="p-datatable-customers"
        emptyMessage="No customers found."
      >

        <CustomDropDownColumn dt={dt} options={options} field={field}/>

      </DataTable>
    </div>
  </div>
);

// ....

创建一个基于类的组件来封装过滤器逻辑。从此类公开 filterElement 并将其作为 Column 组件中的道具应用

接下来,我尝试为每种过滤器类型创建一个类。过滤器类将存储 filterElement 及其所有必需的状态和功能。我的意图是在映射期间为每一列实例化一个新的 DropDownFilter 类,并将其公开的 filterElement 方法提供给正在映射的列。这不起作用,因为在 React 中以这种方式创建类不会挂载该类。因为它已卸载,所以我无法使用 DropDownFilter 类调用 setState。

DropDownFilter.js

import React from 'react'
import { MultiSelect } from 'primereact/multiselect';

class DropDownFilter extends React.Component {
  constructor(props) {
    super(props);
    
    this.state = {
      selectedOptions: null,
    }
  }

  onOptionsChange = (e) => {
    this.props.dt.current.filter(e.value, this.props.field, 'in');
    this.setState({ selectedOptions: e.value })
  }

  filterElement = () => {
    return (
      <React.Fragment>
        <MultiSelect

          value={this.state.selectedOptions}
          options={this.props.options}
          onChange={this.onOptionsChange}
          optionLabel="option"
          optionValue="option"
          className="p-column-filter"

        />
      </React.Fragment>
    )
  }

  render() {
    return (null)
  }

}

export default DropDownFilter;

数据表

// ....

const columnFieldsForMapping = ['id', 'company']

const mappedColumns = columnFieldsForMapping.map((field) => {

    // We will map a drop down filter to each column

    // Get unique values in column to supply to dropdowns
    let options = tableData.map(x => {
      const obj = {}
      obj.option = x[field]
      return obj;
    })
    options = uniq(options)

    // Instantiate instance of DropDownFilter class to use with mapped column
    const newFilter = new DropDownFilter({ dt: dt, field: field, options: options })

    return (

      <Column key={field} field={field} header={field} filter filterElement={newFilter.filterElement} />

    )
  
})

return (
  <div className="datatable-filter-demo">
    <div className="card">
      <DataTable ref={dt} value={tableData} paginator rows={10}
        className="p-datatable-customers"
        emptyMessage="No customers found."
      >

        {mappedColumns}

      </DataTable>
    </div>

  </div>
);
}
4

0 回答 0