question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using mui-datatables in a type-safe way by having access to the original object

See original GitHub issue

(largely based on my similar reply to issue #109, but decided it was more appropriate to start an issue as well to get the discussion rolling)

Hi @gregnb, thank you for your great work on this.

I wonder if it’s possible use mui-datatables in a more “type-safe” way. Maybe it’s already available or maybe it’s something that can be easily implemented.

Currently we specify the columns as such:

const columns = [
  {
    name: 'object_field',
    label: 'Object field label'
  }
}

This will take the object_field field from each object in the array sent to data. However, specifying the field name as a string isn’t type-safe. TypeScript has no way of knowing whether objects in data contain or don’t contain the field object_field.

Question:

Is it possible perhaps, instead of having name as a required field, another option could be added? (let’s call it value, required when name isn’t specified), that would be used as such:

const columns = [
  {
    value: (obj) => obj.object_field,
    label: 'Object field'
  }
}

This way, we could use it with TypeScript:

value: (obj: KnownType) => obj.object_field,

…and TypeScript would check that object_field does indeed belong to KnownType. Since it’s an extra option, it wouldn’t interfere with the current way of doing things or JS-only users.

Would also be nice to get the original data object in customBodyRender. Use case for this is: if we want to render one column based on a value from another.

Right now we do have access to the values array in tableMeta.rowData, The caveat is: the order matters. Changing the column order requires a change in how we access tableMeta.rowData. But would be nice to be able to keep the type information if using TypeScript.

So maybe we could have the original object available in the parameters sent to customBodyRender?

What are your thoughts? Thanks!

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:5
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
erencaycommented, Mar 11, 2019

So maybe we could have the original object available in the parameters sent to customBodyRender?

I second better typescript support and flexible value resolution, but putting those points aside, the lacking source object parameter in customBodyRender method is an appreciable drawback. I wanted to render two fields of an object into one column but I was forced to add the other field to schema as an excluded field. (Which also changed row index, since it matters).

2reactions
dandreicommented, Mar 10, 2019

@gregnb thanks for your prompt reply. In the meanwhile, I’ve written an API that sits in front of the mui-datatables API and does exactly what I wanted 😃.

It takes:

  • A list of objects.
  • Type-aware column definitions (a definition object consists of the column’s label, and two functions: one for formatting the value (used for sorting and searching), the other used to render what gets displayed).

It then produces mui-datatables-compatible data and columns that can be plugged directly into <MUIDataTable />.

What happens under the hood is that I use a hidden ({display: "excluded"}) column to hold the raw object data, which I then get to fully access in the customBodyRender of every cell from the MUIDataTableMeta’s rowData, when rendering.

The data sent to <MUIDataTable /> will be an array of objects with the following structure:

{
  data: /* the original raw object */
  data1: /* value for the first column */
  ...
  dataN: /* value for the Nth column */
}

As mentioned previously, the data1dataN fields will be the values used for sorting and searching, while the original raw object will be used when displaying the data.

Long story short, here’s how I use it:

const definitions: ColumnDefinitions<MyObjectType> = [
  {
    title: "Column 1",
    value: x => x.field1,
    render: x => <>{x.field1}</>
  },
  {
    title: "Column 2",
    value: x => x.field2,
    render: x => <>{x.field2}</>
  },
]

const [columns, data] = muiFormat(definitions, originalData);

Note 1: The functions passed to value and render will know that MyObjectType contains fields field1, field2. Parameter originalData is of type MyObjectType[]

Note 2: Real-life use cases are much more complex than just rendering the raw value. I’m drawing graphs, rendering the same data differently based on the column it’s in, using values from multiple fields to render a single cell, etc. This logic can all be painlessly specified in the definitions object above.

This is the code that’s working behind the scenes, if anyone’s interested:

import {MUIDataTableColumnDef} from "mui-datatables";

type MUIDataTableMeta = {
    rowData: Array<unknown>
    rowIndex: number
}

type CustomBodyRender<T> = (v: T | string | number, a: MUIDataTableMeta) => JSX.Element | string

type ColumnBuilder<T> = {
    name: string,
    label: string,
    render: CustomBodyRender<T>,
    options?: object
}

export function makeMuiColumn<T>({name, label, render, options}: ColumnBuilder<T>): MUIDataTableColumnDef {
    return {
        label,
        name,
        options: {customBodyRender: render, ...options}
    };
}

export type ColumnDefinition<T> = {
    label: string,
    value?: (data: T, rowIndex: number) => string | number,
    render?: (data: T, rowIndex: number) => JSX.Element | string
}

export type ColumnDefinitions<T> = Array<ColumnDefinition<T>>

const getColumn = (i: number) => `data${i}`;

function makeMuiColumns<T>(definitions: ColumnDefinitions<T>) {
    return [
        makeMuiColumn({
            name: "data",
            label: "__DATA__",
            render: () => "",
            options: {display: "excluded"}
        }),

        ...definitions.map((definition, i) => {
            const {label, render} = definition;
            return makeMuiColumn({
                name: getColumn(i),
                label,
                render: (v, a) => {
                    if (render === undefined) {
                        return `${v}`;
                    } else if (Array.isArray(a.rowData)) {
                        const [data] = a.rowData as unknown as [T];
                        return render(data, a.rowIndex);
                    }
                    return "";
                }
            })
        })
    ]
}

type MuiRowData<T> = { data: T } & { [column: string]: string | number }

type MuiTableData<T> = Array<MuiRowData<T>>

function muiData<T>(tableRows: T[], definitions: ColumnDefinitions<T>): MuiTableData<T> {
    return tableRows.map((tableRow, rowIndex) => {
        return {
            ...definitions.reduce((acc, definition, i) => {
                const {value} = definition;
                if (value === undefined) {
                    return {...acc, [getColumn(i)]: ""};
                }
                return {...acc, [getColumn(i)]: value(tableRow, rowIndex)}
            }, {data: tableRow} as unknown as MuiRowData<T>)
        }
    })
}

export function muiFormat<T>(cols: ColumnDefinitions<T>, tableRows: T[]): [MUIDataTableColumnDef[], MuiTableData<T>] {
    return [
        makeMuiColumns(cols),
        muiData(tableRows, cols)
    ]
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

How can I put these object data to the mui-datatable?
You can add the columns to the columns array for the doses and you can add the data for the doses in the...
Read more >
Mui datagrid nested data. Pular para o conteúdo v5 is out ...
Mui datagrid nested data. Pular para o conteúdo v5 is out! Head to thedocumentationto get started. 0-beta. getSelectedRows ()}, on DataGrid ...
Read more >
vb.net search in datatable
Anyway, here's how to access data in the table: C#: Object o = dataTable. Using ExecuteNonQuery ... Using LINQ WHERE clause with DataTable...
Read more >
MUI Datatables: Datatables for React using Material-UI - Morioh
MUI -Datatables is a responsive datatables component built on Material-UI. It comes with features like filtering, resizable columns, view/hide columns, ...
Read more >
Data grid mui. Filtering helps to view a subset of the records ...
Oct 25, 2022 · MUI DataGrid Column Width Column width is simply set with the ... npm install mui -datatables --save Step 3...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found