Ag-grid to show some editable columns vertically stacked without using row span

I need to display a grid with dash-ag-grid in a way similar to row spanning in visuals but opposite in function. There’s a need to have some editable columns to be stacked vertically as on the picture below.

With ordinary html tables there’s a way to change cell style from “display: table-cell” to “display: block” or to “display: inline-block” to obtain desired effect, it comes handy with responsive designs for smaller screens.

Is there a way to obtain similar result with ad-grid without resolving to multiplying rows in dataset and using row spanning?

Row spanning is the last resort as with it there will be trouble mapping values from various rows of not spanned editable columns to database. The imaginable solution with spanned rows will be to have some kind of master detail rendering with details having widgets or grids to edit values for editable columns of selected row. Implementing master-detail seems as an overhead, so some CSS or ad-grid params based solution is desirable.

1 Like

Hello @gulimaster,

Welcome to the community!

You can check out here:

The data would need to be replicated, but the data can span across if repeated. Might be a little more complex for your case…

Or… you could use columnGroups, or create a custom cell renderer that would allow you to display the items in stacked fashion. There are a couple of different options available to you.

I dont know how your data is setup, but you could potentially do a cellRenderer:

dagcomponentfuncs.multiStack = (props) => {
    if (!props.data) {
       return null
    }
    return React.createElement('div', {style: {display: 'flex', flexDirection: 'column'}}, [params.data.col1, params.data.col2, params.data.col3])
}

To edit this, would be a little bit more intense, but still possible.

Hi. thx for your reply! I really hoped that I just missed a built-in function for this :slight_smile: Already tried your options, they turned out to be a little inappropriate for my case. But since there are no other options, it’s worth trying again. Is there a possibility that developers could take this case up for discussion for implementation? :slight_smile:

@gulimaster,

Could you please elaborate on how you find my suggestion inappropriate?

The Grid is not a html table, but is a set of divs which are coded in a specific fashion in order to display similarly to a html table. The main difference is that you cannot just migrate rows or columns by simply updating styling,

My suggestion was to provide a cellRenderer that could bring the multiple columns into a single stacked column.

This is not a standard request to display the data, and typically this is in reference to utilizing row spanning, which, according to how you have it, these are separate data points in the same line that you want to display a group vertically for a couple of the data points.

eg:
{co1: data1, col2: data2, col3: data3, col4: data4, col5: data5, col6: data6}

^ is this correct?

In Excel, to replicate, you would have to merge the rows to the right and the left to emulate what you are after, each time you want to repeat. My recommendation was to create one cellRenderer that would handle what you are after.

2 Likes

^ Yes you are right
The problem is on my side here. When I use cellRenderer or rowSpan, my web page freezes completely. I didn’t delve deeply into this issue, so I decided to write to the forum and find out maybe I missed another functionality. Maybe you have any ideas why cellRenderer or rowSpan might cause lags?

1 Like

Normally if the site is crashing, it means that something isnt working properly as far as the rendering and is causing it to just spin and spin because the grid cant finish rendering.

Do you have some data that I could use to make a cellRenderer?

I tried it again with cellRenderer and displayed the contents in one cell, thanks!

But is it possible to edit them separately as columns?

I wanna get something like this:

Yes.

This is where if you give me some data I can get you set up.

:grin:

You can also provide your app and js file too ( just the stuff that will make a grid)

1 Like

Please have a look :pray:
This is a py function that generates the grid, there are also columns and records here:

def create_table_journal(df):
    df_columns = ['log_dtime', 'log_dtime_', 'who_fixes', 
                  'jira_link', 'comment_', 'fixed_at', 
                  'error', 'process_name', 'info', 'launch_id_']
    columns_edit = ['who_fixes', 'jira_link', 'comment_']
    
    columns = []
    for c in df_columns:
        column = {
            'headerName': c,
            'field': c,
            'editable': False if c not in columns_edit else True,
            'hide': True if c in ['log_dtime', 'process_name'] else False,
            'flex': 0.7,
            'cellStyle': {
                'white-space': 'pre-wrap'
            }
        }

        if c == 'error':
            column['flex'] = 5
        if c == 'info':
            column['flex'] = 3

        columns.append(column)

    columns.insert(2, {
        "field": "multiStack",
        "cellRenderer": "multiStack"
    })

    df_records = [
        {'log_dtime': '2024-01-19 14:46:08.338183', 'log_dtime_': '2024-01-19\n14:02:08', 'who_fixes': '@gulimaster',
         'jira_link': 'some_link', 'comment_': 'reset', 'fixed_at': None, 'error': 'rejected rows: 204',
         'process_name': 'microservice', 'info': 'microservice:SOURCE\n\n--extract\n\n', 'launch_id_': 23125},
        {'log_dtime': '2024-01-19 14:46:08.338183', 'log_dtime_': '2024-01-19\n14:03:58', 'who_fixes': None,
         'jira_link': None, 'comment_': None, 'fixed_at': None, 'error': 'rejected rows: 204',
         'process_name': 'microservice', 'info': 'microservice:SOURCE\n\n--extract\n\n', 'launch_id_': 23126},
        {'log_dtime': '2024-01-19 14:46:08.338183', 'log_dtime_': '2024-01-19\n14:05:36', 'who_fixes': None,
         'jira_link': None, 'comment_': None, 'fixed_at': None, 'error': 'rejected rows: 204',
         'process_name': 'microservice', 'info': 'microservice:SOURCE\n\n--extract\n\n', 'launch_id_': 23127},
        {'log_dtime': '2024-01-19 14:46:08.338183', 'log_dtime_': '2024-01-19\n14:06:38', 'who_fixes': '@gulimaster',
         'jira_link': None, 'comment_': 'reset', 'fixed_at': '2024-01-19\n14:06:38', 'error': 'rejected rows: 204',
         'process_name': 'microservice', 'info': 'microservice:SOURCE\n\n--extract\n\n', 'launch_id_': 23128},
        {'log_dtime': '2024-01-19 14:46:08.338183', 'log_dtime_': '2024-01-19\n14:06:47', 'who_fixes': '@gulimaster',
         'jira_link': None, 'comment_': 'reset', 'fixed_at': '2024-01-19\n14:06:38', 'error': 'rejected rows: 204',
         'process_name': 'microservice', 'info': 'microservice:SOURCE\n\n--extract\n\n', 'launch_id_': 23129}]

    table = html.Div(
        [dag.AgGrid(
            id='dash_table-journal',
            rowData=df_records,
            columnDefs=columns,
            defaultColDef={
                "resizable": True,
                "sortable": True,
                "filter": True,
                'wrapText': True,
                'autoHeight': True,
                'singleClickEdit': True,
                "cellStyle": {
                    'display': 'flex',
                    'justifyContent': 'flex-start',
                    'alignItems': 'center',
                    'fontSize': '13px',
                    'fontWeight': '400',
                    'padding': '10px',
                    'lineHeight': 'normal',
                    'border': '0.5px solid rgba(0,0,0,0.1)',
                }
            },
            dashGridOptions={
                "enableCellTextSelection": True,
                "ensureDomOrder": True,
                "domLayout": "autoHeight"
            },
            style={
                'height': None
            }
        )]
    )

    return table

And cellRenderer js function:

var dagcomponentfuncs = window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {};

dagcomponentfuncs.multiStack = (props) => {
    if (!props.data) {
       return null
    }

    createColumn = (column) => {
        return React.createElement('div',
            {
                style: {display: 'flex', justifyContent: 'center', alignItems: 'center'},
                colId: column
            }, [props.data[column]])
    }

    return React.createElement('div', {style: {display: 'flex', flexDirection: 'column'}},
                               [createColumn('who_fixes'), createColumn('jira_link'), createColumn('comment_')])
}

Try something like this:

import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, State

app = Dash(__name__)

rowData = [
    dict(account='account a',  balance=522.31, name='Homer', grade="25"),
    dict(account='account b',  balance=1607.9, name='Marge', grade=90),
    dict(account='account c',  balance=-228.41, name='Lisa', grade=100),
]


columnDefs = [
    {
        "field": "account",
        "valueFormatter": {"function": "(params.value).toUpperCase();"},
        "editable": True,
    },
    {
        "headerName": "balance/name/grade",
        "cellRenderer": "MultiStack",
    },
    {'field': 'balance', 'hide': True},
    {'field': 'name', 'hide': True},
    {'field': 'grade', 'hide': True}
]

defaultColDef = {
    "resizable": True,
}

app.layout = html.Div(
    [
        dcc.Markdown(
            "This demonstrates some basic string formatting functions"
        ),
        dag.AgGrid(
            id="grid",
            columnDefs=columnDefs,
            rowData=rowData,
            columnSize="sizeToFit",
            defaultColDef=defaultColDef,
            dashGridOptions={'rowHeight': 120},
        ),
        html.Div(id='output')
    ],
    style={"margin": 20},
)

app.clientside_callback(
    """
    (c, d) => {return JSON.stringify(d)}
    """,
    Output('output', 'children'),
    Input('grid', 'cellValueChanged'),
    State('grid', 'rowData')
)


if __name__ == "__main__":
    app.run(debug=True)

js:

var dagcomponentfuncs = window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {};


dagcomponentfuncs.MultiStack = (props) => {
    if (!props.data) {
        return null
    }

    stack = []
    const cols = ['balance', 'name', 'grade']
    cols.forEach((col) => {
        var onChange = (e) => {
            props.node.setDataValue(col, e.target.value)
        }
        stack.push(React.createElement('input',{value: props.data[col],
        onChange, key: `${props.rowIndex} - ${col}`}))
    })
    return React.createElement('div', {style:{'display': 'flex', 'flexDirection': 'column'}}, stack)
}

Thanks! You’re a real hero! :love_you_gesture:

1 Like