ReactJS: AG-GRID: динамическое назначение редактора ячеек на основе метаданных

Я хочу применить редактор ячеек динамически на основе метаданных. Например, если строка определена как числовая (т.е. строка, содержащая целочисленные данные), я хочу использовать числовой редактор. Для даты, редактора даты и т. Д.

Я буду знать только во время выполнения определение каждой строки, и поскольку каждая строка может содержать свой собственный тип данных, применение редактора ячеек на уровне столбца не будет работать, если редактор является конкретным типом данных (я пытался определить общий редактор на уровне столбца, но не чувствую, что у меня достаточно компетенции, чтобы это произошло, и у меня нет достаточно «реального» кода, чтобы показать пример).

Вот моя составляющая:

import React, { Component } from 'react';
import axios from 'axios';

import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-balham.css';

import NumericEditor from "./numericEditor.jsx";

import DatePicker from 'react-date-picker'


const API = 'http://localhost:53884/api/UserConfig/UI'
var PARMS = '';

class UserConfig extends Component {
    constructor(props) {
        super(props);

        this.state = {
            columnDefs: [],
            components: null,
            rowData: null,
            isLoading: false,
            error: null,
            config: [],
            heading: "",
            meta: [],
            frameworkComponents: {
                numericEditor: NumericEditor,
                datePicker: DatePicker

            }
        };
        this.onGridReady = this.onGridReady.bind(this);
    }

    onGridReady(params) {
        this.gridApi = params.api;
        this.columnApi = params.columnApi;

        this.gridApi.sizeColumnsToFit();
    }

    componentDidMount() {
        this.setState({ isLoading: true });

        axios.get(API + PARMS)
            .then(fubar => {

                const config = fubar.data.config;
                const headerRow = fubar.data.header;
                const rowData = fubar.data.results;
                const meta = fubar.data.meta;

                this.setState({ heading: "User Configuration: Trade Date " + new Date(config.tradeDate).toLocaleDateString("en-US") });

                var newCols = [
                    { headerName: "", field: "attribute", width: 200, hide: true },
                    { headerName: "", field: "displayName", width: 200, resizable: true, sortable: true, filter: true },
                    { headerName: headerRow.value0, field: "value0", width: 100, resizable: true, editable: true },
                    { headerName: headerRow.value1, field: "value1", width: 100, resizable: true, editable: false },
                    { headerName: headerRow.value2, field: "value2", width: 100, resizable: true, editable: false },
                    { headerName: headerRow.value3, field: "value3", width: 100, resizable: true, editable: false },
                    { headerName: headerRow.value4, field: "value4", width: 100, resizable: true, editable: false },
                    { headerName: headerRow.value5, field: "value5", width: 100, resizable: true, editable: false },
                    { headerName: headerRow.value6, field: "value6", width: 100, resizable: true, editable: false },
                    { headerName: headerRow.value7, field: "value7", width: 100, resizable: true, editable: false },
                    { headerName: headerRow.value8, field: "value8", width: 100, resizable: true, editable: false },
                    { headerName: headerRow.value9, field: "value9", width: 100, resizable: true, editable: false },
                ];

                if (headerRow.value0 === null) newCols[2].hide = true;
                if (headerRow.value1 === null) newCols[3].hide = true;
                if (headerRow.value2 === null) newCols[4].hide = true;
                if (headerRow.value3 === null) newCols[5].hide = true;
                if (headerRow.value4 === null) newCols[6].hide = true;
                if (headerRow.value5 === null) newCols[7].hide = true;
                if (headerRow.value6 === null) newCols[8].hide = true;
                if (headerRow.value7 === null) newCols[9].hide = true;
                if (headerRow.value8 === null) newCols[10].hide = true;
                if (headerRow.value9 === null) newCols[11].hide = true;

                this.setState({ rowData, config, columnDefs: newCols, meta });
            })
            .catch(error => this.setState({
                error,
                isLoading: false
            }));
    }

    onCellEditingStarted = params => {
        const { meta } = this.state;

        const attribute = params.data.attribute;

        var cols = this.state.columnDefs;

        const metaRow = meta.find(item => { return item.attribute === attribute });

        if (metaRow.dataType === "datetime") cols[2].cellEditor = "datePicker";
        if (metaRow.dataType === "int") cols[2].cellEditor = "numericEditor";

        this.setState({ columnDefs: cols });

        //NumericEditor(params);

        //alert('Display Name = ' + metaRow.displayName + ',\n'
        //    + 'Display Order = ' + metaRow.displayOrder + ',\n'
        //    + 'Default Value = ' + metaRow.defaultValue + ',\n'
        //    + 'Data Type = ' + metaRow.dataType + ',\n'
        //    + 'Allow Nulls = ' + metaRow.allowNull);
    };

    render() {
        const { heading, rowData, columnDefs } = this.state;

        return (
            <div className="ag-theme-balham" style={{ height: '500px', width: '800px' }} >
                <h2 style={{ paddingLeft: '32px' }}>{heading}</h2>

                <AgGridReact
                    columnDefs={columnDefs}
                    rowData={rowData}
                    onCellClicked={this.onCellClicked.bind(this)}
                    frameworkComponents={this.state.frameworkComponents}
                />
            </div>

        );
    }
}

export default UserConfig;

Мой подход заключается в использовании onCellClicked:

    <AgGridReact
        columnDefs={columnDefs}
        rowData={rowData}
        onCellClicked={this.onCellClicked.bind(this)}
        frameworkComponents={this.state.frameworkComponents}
    />

чтобы применить изменение редактора ячеек в обработчике событий ниже:

onCellClicked = params => {
    const { meta } = this.state;

    const attribute = params.data.attribute;

    var cols = this.state.columnDefs;

    const metaRow = meta.find(item => { return item.attribute === attribute });

    if (metaRow.dataType === "datetime") cols[2].cellEditor = "datePicker";
    if (metaRow.dataType === "int") cols[2].cellEditor = "numericEditor";

    this.setState({ columnDefs: cols });
};

Каждый раз, когда щелкают ячейку, тип данных определяется для строки ячейки, а редактор ячеек для столбца [2] изменяется, чтобы отразить тип данных строки ячейки.

Мне это кажется жестким подходом, но я не знаю, как еще оставаться в мире ReactJS / AG-Grid.

Между прочим, к тому времени, когда кто-то сможет щелкнуть ячейку, состояние columnDefs уже будет установлено дважды: щелчок по ячейке будет представлять третий + раз.

========== РЕДАКТИРОВАТЬ ==========

Я обнаружил, что резкое изменение структуры cols заставит приложение применить мои изменения в редакторе ячеек. Например, при копировании моего columnDefs в новый массив я нарезаю несколько столбцов, а не беру весь массив:

var cols = this.state.columnDefs.slice(0,8);

когда я устанавливаю состояние столбца,

this.setState({ columnDefs: cols });

мой выбор редактора вступает в силу.

Конечно, я не хочу - да и не должен - вносить такие радикальные изменения в определения моих столбцов. Если бы я это сделал, я мог бы подумать о добавлении нового столбца каждый раз, когда кто-то щелкает ячейку, возможно, если тип данных целевой строки не совпадает с типом данных последней нажатой. Но это кажется действительно плохим подходом.

Другой подход, с которым я не добился успеха, - это принудительное обновление, используя:

this.forceUpdate();
this.setState({ state: this.state });

но ни то, ни другое здесь не кажется эффективным.

Я ценю ваш отзыв - спасибо.


person Bill Roberts    schedule 11.06.2019    source источник


Ответы (1)


Я просто искал что-то подобное, и в документации ag-grid говорится об этой встроенной функциональности. См. Раздел «Многие редакторы в одном столбце» в документации ag-grid (v22.0.0): https://www.ag-grid.com/javascript-grid-cell-editing/#many-editors-one-column

Многие редакторы в одной колонке

Также можно использовать разные редакторы для разных строк в одном столбце. Обычно приложение может проверять содержимое строк и соответственно выбирать редактор. Чтобы настроить этот параметр colDef.cellEditorSelector на функцию, которая возвращает имя компонента, который будет использоваться в качестве редактора, и, при необходимости, настраиваемые параметры, которые будут переданы в него

Параметры, передаваемые в эту функцию, такие же, как параметры, передаваемые редакторам ячеек.

В следующем примере показано, как использовать разные редакторы и параметры в одном столбце. Обратите внимание, что:

  • Столбец «Значение» содержит данные разных типов, как показано в столбце «Тип» (числа / пол / настроения).
  • colDef.cellEditorSelector - это функция, которая возвращает имя компонента, используемого для редактирования, в зависимости от типа данных для этой строки.
cellEditorSelector:function (params) {

  if (params.data.type === 'age') return {
      component: 'numericCellEditor'
  };

  if (params.data.type === 'gender') return {
      component: 'agRichSelect',
      params: {values: ['Male', 'Female']}
  };

  if (params.data.type === 'mood') return {
      component: 'agRichSelect'
  };

  return null;
}
person Piers Geyman    schedule 05.12.2019