Dash AG Grid automatic changes columns order with columnSize as sizeToFit

I’m trying to use Ag Grid to show data that was filtered by dmc.DatePicker and I got a problem as below:

Code:

import dash_ag_grid as dag
from dash import Dash, html, dcc
import pandas as pd
import dash_mantine_components as dmc
from datetime import datetime, date
from dash import Dash, Input, Output, State, dcc, html 

app = Dash(__name__)

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv")
df['date'] = pd.to_datetime(df['date'], format='%d/%m/%Y')
df['date_2'] = pd.to_datetime(df['date'], format='%d/%m/%Y').dt.strftime('%Y%m%d').astype(int)

app.layout = dmc.MantineProvider([
    dmc.Grid([
        dmc.Col([
            dmc.DatePicker(id='date1', 
                           label='Date1', 
                           description='Select date 1', 
                           value=date(2002,2,24), 
                           style={'width':200})
        ], span='auto'),
        dmc.Col([
            dmc.DatePicker(id='date2', 
                           label='Date2', 
                           description='Select date 2', 
                           value=date(2004,8,29), 
                           style={'width':200})
        ], span='auto'),
        dmc.Col([
            dmc.DatePicker(id='date3', 
                           label='Date3', 
                           description='Select date 3', 
                           value=date(2006,2,26), 
                           style={'width':200})
        ], span='auto'),
        dmc.Col([
            dmc.DatePicker(id='date4', 
                           label='Date4', 
                           description='Select date 4', 
                           value=date(2012,8,12), 
                           style={'width':200})
        ], span='auto'),
        dmc.Col([
            dmc.Button("Submit", color='gray', id='btn')], span='auto')
    ], align = 'center'),
    dmc.Grid([
        dmc.Col([
            html.Div(id='table')])
    ])
])

@app.callback(Output('table', 'children'),
             Input('btn', 'n_clicks'),
             State('date1', 'value'),
             State('date2', 'value'),
             State('date3', 'value'),
             State('date4', 'value'), prevent_inital_call = True)
def update_data(n_clicks, date1, date2, date3, date4):
    if n_clicks:
        df3 = df[df['date'].isin([date1, date2, date3, date4])]
        date = df3['date_2'].unique().astype(str)
        bs_dt_1 = date[0]
        bs_dt_2 = date[1]        
        bs_dt_3 = date[2]         
        bs_dt_4 = date[3]
        
        df4 = pd.pivot_table(df3, values = ['gold', 'silver', 'bronze', 'total'], index='sport', columns='date_2').reset_index()
        df4.columns = df4.columns.map('{0[0]}_{0[1]}'.format)

        return dag.AgGrid(id='table_2', 
                         rowData = df4.to_dict('records'),
                         columnDefs=[{'field':'sport_'}, 
                                    {'field':'bronze_' + bs_dt_1, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'bronze_' + bs_dt_2, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'bronze_' + bs_dt_3, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'bronze_' + bs_dt_4, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'gold_' + bs_dt_1, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'gold_' + bs_dt_2, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'gold_' + bs_dt_3, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'gold_' + bs_dt_4, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'silver_' + bs_dt_1, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'silver_' + bs_dt_2, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'silver_' + bs_dt_3, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}, 
                                    {'field':'silver_' + bs_dt_4, 
                                     "valueFormatter": {'function': """d3.format(",.2f")(params.value)"""}}], 
                          defaultColDef = {"resizeable":True},
                          columnSize = 'sizeToFit')

if __name__ == "__main__":
    app.run_server(debug=False, port=1414, jupyter_mode='external')

For the first load, it showed columns order same as I set in code:

When I changed date 4 to 2010-02-28, columns order changed as below and it not fit anymore:

How can I keep columns order as first load?

Thank you.

Hello @hoatran,

I would recommend also sending a resetColumnState in your returned grid.

You are essentially trying to destroy and rebuild your grid each time, this is not how the grid itself responds but just adjusts accordingly to the new settings that you have passed. Since you are removing and adding columnDefs, you are conflicting with the grid’s columnState.

I would also recommend ditching the sizeToFit and utilize flex inside the columnDefs as it is more reliable, as well as gives quite a bit of flexibility.

1 Like

Here is a version that might work for you. XD

import dash_ag_grid as dag
from dash import Dash, html, dcc
import pandas as pd
import dash_mantine_components as dmc
from datetime import datetime, date
from dash import Dash, Input, Output, State, dcc, html

app = Dash(__name__)

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv")
df['date'] = pd.to_datetime(df['date'], format='%d/%m/%Y')
df['date_2'] = pd.to_datetime(df['date'], format='%d/%m/%Y').dt.strftime('%Y%m%d').astype(int)

app.layout = dmc.MantineProvider([
    dmc.Grid([
        dmc.Col([
            dmc.DatePicker(id='date1',
                           label='Date1',
                           description='Select date 1',
                           value=date(2002, 2, 24),
                           style={'width': 200})
        ], span='auto'),
        dmc.Col([
            dmc.DatePicker(id='date2',
                           label='Date2',
                           description='Select date 2',
                           value=date(2004, 8, 29),
                           style={'width': 200})
        ], span='auto'),
        dmc.Col([
            dmc.DatePicker(id='date3',
                           label='Date3',
                           description='Select date 3',
                           value=date(2006, 2, 26),
                           style={'width': 200})
        ], span='auto'),
        dmc.Col([
            dmc.DatePicker(id='date4',
                           label='Date4',
                           description='Select date 4',
                           value=date(2012, 8, 12),
                           style={'width': 200})
        ], span='auto'),
        dmc.Col([
            dmc.Button("Submit", color='gray', id='btn')], span='auto')
    ], align='center'),
    dmc.Grid([
        dmc.Col([
            html.Div(id='table')])
    ])
])


@app.callback(Output('table', 'children'),
              Input('btn', 'n_clicks'),
              State('date1', 'value'),
              State('date2', 'value'),
              State('date3', 'value'),
              State('date4', 'value'), prevent_inital_call=True)
def update_data(n_clicks, date1, date2, date3, date4):
    if n_clicks:
        df3 = df[df['date'].isin([date1, date2, date3, date4])]
        # date = df3['date_2'].unique().astype(str)
        # bs_dt_1 = date[0]
        # bs_dt_2 = date[1]
        # bs_dt_3 = date[2]
        # bs_dt_4 = date[3]

        df4 = pd.pivot_table(df3, values=['gold', 'silver', 'bronze', 'total'], index='sport',
                             columns='date_2').reset_index()
        df4.columns = df4.columns.map('{0[0]}_{0[1]}'.format)

        columnDefs = [{'field': 'sport_'}]
        for t in ['bronze', 'gold', 'silver']:
            for dt in [date1, date2, date3, date4]:
                if dt:
                    columnDefs.append({'field': f'{t}_{dt.replace("-", "")}',
                                         "valueFormatter": {'function': """params.value ? d3.format(",.2f")(params.value) : '0.00'"""}})

        return dag.AgGrid(id='table_2',
                          rowData=df4.to_dict('records'),
                          columnDefs=columnDefs,
                          defaultColDef={"resizeable": True, 'flex': 1},
                          columnSize=None,
                          resetColumnState=True
                          )


if __name__ == "__main__":
    app.run_server(debug=True)
2 Likes

Thank you @jinnyzor for always helping me in Ag Grid problem :smiley:

2 Likes