Updating Column Definitions

The Column Definitions page demonstrated how to set the
initial column definitions for a grid. Here, we look at how to update column definitions after they’ve been initially
set.

Add and Remove Columns

To add or remove columns, use a callback to update the grid’s column definitions by using the grid’s columnDefs
property as an output.

In this example, you can use the button to toggle between including and excluding the columns that show the numbers for
each medal type. Note how any state applied to a column (for example, sort, filter, and width) is retained if the column
still exists after the new definitions are applied.

import dash_ag_grid as dag
from dash import Dash, html, Input, Output, callback
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

medals_included = ["athlete", "total", "gold", "silver", "bronze"]
medals_excluded = ["athlete", "total"]

columns_medals_included = [{"field": i} for i in medals_included]
columns_medals_excluded = [{"field": i} for i in medals_excluded]

app.layout = html.Div(
    [
        html.Button("Exclude medals", id="medal-toggle", n_clicks=0),
        dag.AgGrid(
            id="toggle-medals-columns",
            rowData=df.to_dict("records"),
            defaultColDef={"sortable": True, "resizable": True, "editable": True},
            columnDefs=columns_medals_included,
            columnSize="sizeToFit",
        ),
    ]
)


@callback(
    Output("toggle-medals-columns", "columnDefs"),
    Output("medal-toggle", "children"),
    Input("medal-toggle", "n_clicks"),
)
def toggle_cols(n):
    if n and n % 2 != 0:
        return columns_medals_excluded, "Include Medals"
    return columns_medals_included, "Exclude Medals"


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

Update Properties

All properties of a column definition can be updated. For example, if you want to change the header name of a column,
update the headerName on its column definition within a callback and use the grid’s columnDefs as an output on the
callback.

The example below demonstrates updating column definitions to change how columns are configured.

Note the following:

import dash_ag_grid as dag
from dash import Dash, html, Input, Output, State, callback
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs = [
    {"field": "country"},
    {"field": "year"},
    {"field": "athlete"},
    {"field": "age"},
    {"field": "sport"},
    {"field": "total"},
]

columnDefs_leet = [
    {"field": "country", 'headerName': 'C0un7ry'},
    {"field": "year", 'headerName': 'Y34r'},
    {"field": "athlete", 'headerName': '4thl3t3'},
    {"field": "age", 'headerName': '4g3'},
    {"field": "sport", 'headerName': '5p0r7'},
    {"field": "total", 'headerName': 'T0t4l'},
]

app.layout = html.Div(
    [
        html.Button("L33t my h34d3rs", id="btn-header-names-toggle"),
        html.Button("Set Value Formatters", id="btn-value-format-toggle"),
        dag.AgGrid(
            id="updating-column-props",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs,
            defaultColDef={"sortable": True, "resizable": True, "filter": True},
            columnSize="sizeToFit",
        ),
    ]
)


@callback(
    Output("updating-column-props", "columnDefs"),
    Output("btn-header-names-toggle", "children"),
    Input("btn-header-names-toggle", "n_clicks"),
    prevent_initial_call=True
)
def toggle_cols(n):
    if n % 2 == 0:
        return columnDefs, "L33t my h34d3rs"
    return columnDefs_leet, "Remove header names"


@callback(
    Output("updating-column-props", "columnDefs", allow_duplicate=True),
    Output("btn-value-format-toggle", "children"),
    Input("btn-value-format-toggle", "n_clicks"),
    State("updating-column-props", "columnDefs"),
    prevent_initial_call=True
)
def toggle_vals(n, column_defs):
    if n % 2 == 0:
        for col in column_defs:
            col["valueFormatter"] = None
        return column_defs, "Set Value Formatters"

    for col in column_defs:
        col["valueFormatter"] = {"function": "'* ' + params.value + ' *'"}
    return column_defs, "Remove Value Formatters"


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

Partial Update

Dash 2.9 introduced Partial Property Updates using the Patch
class.

This feature allows to update only a part of a property of a component. Thus, it is possible to use a Patch to make a
partial update on the columnDefs.

Here is an example using the Patch class to update only the header of the first column.

import dash_ag_grid as dag
from dash import Dash, html, Input, Output, State, dcc, Patch, callback
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs = [
    {"field": "country"},
    {"field": "year"},
    {"field": "athlete"},
    {"field": "age"},
    {"field": "sport"},
    {"field": "total"},
]

app.layout = html.Div(
    [
        html.Div("Set a new header name for the first column:"),
        dcc.Input(id='input-name', type="text", placeholder="New Header Name", style={'margin': 5}),
        html.Button("Set New Header Name", id="btn-set-name"),
        dag.AgGrid(
            id="updating-column-props-partial",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs,
            defaultColDef={"suppressMovable": True, "sortable": True, "resizable": True},
            columnSize="sizeToFit",
        ),
    ]
)


@callback(
    Output("updating-column-props-partial", "columnDefs"),
    Input("btn-set-name", "n_clicks"),
    State("input-name", "value"),
    prevent_initial_call=True
)
def toggle_cols(n, new_name):
    patched_grid = Patch()
    patched_grid[0]['headerName'] = new_name or columnDefs[0]['field'].capitalize()
    return patched_grid


if __name__ == "__main__":
    app.run(debug=True)
Set a new header name for the first column:

Update Stateful Properties

Some column properties are stateful properties, meaning their values can change while the user is interacting with the grid.
Those properties are handled by the grid property columnState. See
the Column State page for more information.

They can be defined with columnDefs using either their Initial Attribute or their Stateful Attribute:

The stateful properties of columnDefs and their corresponding initial properties are listed below:

Stateful Attribute Initial Attribute Description
aggFunc initialAggFunc The function to aggregate this column by if row<br>grouping or pivoting.
flex initialFlex The flex value for setting this column’s width.
hide initialHide Whether this column should be hidden.
pinned initialPinned Whether this column should be pinned.
pivot initialPivot If this column should be a pivot.
pivotIndex initialPivotIndex Whether this column should be a pivot and in what order.
rowGroup initialRowGroup Whether this column should be a row group.
rowGroupIndex initialRowGroupIndex Whether this column should be a row group and in what order.
sort initialSort The sort to apply to this column.
sortIndex initialSortIndex The order to apply sorting, if multi column sorting.
width initialWidth Width of the column.

Here is an example showing the difference between initial and stateful column definition properties.

Note the following:

import dash_ag_grid as dag
from dash import Dash, html, Input, Output, callback
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs = [
    {"field": "country"},
    {"field": "year", 'initialSort': 'desc', 'initialWidth': 100},
    {"field": "athlete", 'pinned': 'left', 'width': 140},
    {"field": "age"},
    {"field": "sport"},
    {"field": "total"},
]

app.layout = html.Div(
    [
        html.Button("Apply initial props", id="btn-init-props-stateful"),
        html.Button("Remove columns", id="btn-remove-cols"),
        dag.AgGrid(
            id="updating-column-stateful-props",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs,
            defaultColDef={"sortable": True, "resizable": True},
        ),
    ]
)


@callback(
    Output("updating-column-stateful-props", "columnDefs"),
    Input("btn-init-props-stateful", "n_clicks"),
)
def init_props(_):
    return columnDefs


@callback(
    Output("updating-column-stateful-props", "columnDefs", allow_duplicate=True),
    Input("btn-remove-cols", "n_clicks"),
    prevent_initial_call=True,
)
def clear_grid(_):
    return []


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

If only stateful properties should be updated, consider to use columnState instead of columnDefs.
(see Column State page)

Maintain Column Order

When Column Definitions are provided to the grid, the order of the columns inside the grid is set to match the order of
the newly provided Column Definitions. This means every time columns are set, the order is guaranteed to match the order
of the Column Definitions. This is usually the desired and expected behaviour.

You may wish for the order of the columns to not match the Column Definitions. For example suppose the user has
rearranged columns to their desired order, and then the application updates the Column Definitions (for example, changes the
Cell Renderer used), then it would be undesirable to reset the column order, as the user’s arranged order would be lost.

If the desired behaviour is that column’s order should be maintained, set the grid property :

dashGridOptions = {"maintainColumnOrder": True}

Here is an example showing the difference between maintaining the column order or not while setting columnDefs.

Note the following:

import dash_ag_grid as dag
from dash import Dash, html, Input, Output, dcc, callback
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs = [
    {"field": "country"},
    {"field": "year"},
    {"field": "athlete"},
    {"field": "age"},
    {"field": "sport"},
    {"field": "total"},
]

columnDefs_leet = [
    {"field": "athlete", 'headerName': '4thl3t3'},
    {"field": "age", 'headerName': '4g3'},
    {"field": "country", 'headerName': 'C0un7ry'},
    {"field": "year", 'headerName': 'Y34r'},
    {"field": "sport", 'headerName': '5p0r7'},
    {"field": "total", 'headerName': 'T0t4l'},
]

app.layout = html.Div(
    [
        dcc.Checklist(
            id="chk-maintain",
            options=[{"label": "Enable maintain column order", "value": "Enable"}],
            value=["Enable"],
        ),
        html.Button("L33t my h34d3rs", id="btn-init-props-maintain-order"),
        dag.AgGrid(
            id="updating-column-maintain-order",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs,
            defaultColDef={"sortable": True, "resizable": True},
            columnSize="sizeToFit",
            dashGridOptions={"maintainColumnOrder": True},
        ),
    ]
)


@callback(
    Output("updating-column-maintain-order", "dashGridOptions"),
    Input("chk-maintain", "value"),
    prevent_initial_call=True
)
def maintain_col_order(maintain_ON):
    if maintain_ON:
        return {"maintainColumnOrder": True}
    return {"maintainColumnOrder": False}


@callback(
    Output("updating-column-maintain-order", "columnDefs"),
    Output("btn-init-props-maintain-order", "children"),
    Input("btn-init-props-maintain-order", "n_clicks"),
    prevent_initial_call=True
)
def toggle_cols(n):
    if n % 2 == 0:
        return columnDefs, "L33t my h34d3rs"
    return columnDefs_leet, "Remove header names"


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

If there are new columns added (for example, the new set of columnDefs has additional columns to those currently present),
then these new columns will always be added at the end.

Update Column Groups

Column groups can be updated in the same way as columns, by updating the column group definition.

Here is an example switching between two columnDefs sets using column groups.
These two sets are the same except that the first has the additional column group property 'openByDefault': True for
both groups which expands the groups when setting columnDefs

import dash_ag_grid as dag
from dash import Dash, html, Input, Output, callback
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs_expanded = [
    {
        "headerName": "Athlete",
        'openByDefault': True,
        "children": [
            {'field': 'athlete', 'headerName': 'Name'},
            {'field': 'age', 'columnGroupShow': 'open'},
            {'field': 'country', 'columnGroupShow': 'open'},
        ],
    },
    {
        "headerName": "Results",
        'openByDefault': True,
        "children": [
            {'field': 'sport'},
            {'field': 'gold', 'columnGroupShow': 'open'},
            {'field': 'silver', 'columnGroupShow': 'open'},
            {'field': 'bronze', 'columnGroupShow': 'open'},
            {'field': 'total'},
        ],
    },
]

columnDefs_collapsed = [
    {
        "headerName": "Athlete",
        "children": [
            {'field': 'athlete', 'headerName': 'Name'},
            {'field': 'age', 'columnGroupShow': 'open'},
            {'field': 'country', 'columnGroupShow': 'open'},
        ],
    },
    {
        "headerName": "Results",
        "children": [
            {'field': 'sport'},
            {'field': 'gold', 'columnGroupShow': 'open'},
            {'field': 'silver', 'columnGroupShow': 'open'},
            {'field': 'bronze', 'columnGroupShow': 'open'},
            {'field': 'total'},
        ],
    },
]

app.layout = html.Div(
    [
        html.Button("Collapse All", id="btn-toggle-cols"),
        dag.AgGrid(
            id="updating-column-group",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs_expanded,
            defaultColDef={"sortable": True, "resizable": True, "filter": True},
            columnSize="sizeToFit",
        ),
    ]
)


@callback(
    Output("updating-column-group", "columnDefs"),
    Output("btn-toggle-cols", "children"),
    Input("btn-toggle-cols", "n_clicks"),
    prevent_initial_call=True,
)
def toggle_cols(n):
    if n % 2 == 0:
        return columnDefs_expanded, "Collapse All"
    return columnDefs_collapsed, "Expand All"


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

For expandable groups, to maintain the open/closed state while switching columnDefs, the column group
property groupId have to be assigned in the column group definition.

columnDefs = [
    {
        "headerName": "Group A",
        "groupId": "groupA",
        "children": [
            {"field": "name"},
            {"field": 'age', "columnGroupShow": "open"}
        ]
    }
]

The following example shows this behavior using groupId.

import dash_ag_grid as dag
from dash import Dash, html, Input, Output, ctx, callback
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs1 = [
    {
        "headerName": "Group A",
        "groupId": "groupA",
        "children": [
            {"field": "athlete"},
            {"field": "age"},
            {"field": "country", "columnGroupShow": "open"},
        ],
    },
    {
        "headerName": "Group B",
        "children": [
            {"field": "sport"},
            {"field": "year"},
            {"field": "date", "columnGroupShow": "open"},
        ],
    },
    {
        "headerName": "Group C",
        "groupId": "groupC",
        "children": [
            {"field": "total"},
            {"field": "gold", "columnGroupShow": "open"},
            {"field": "silver", "columnGroupShow": "open"},
            {"field": "bronze", "columnGroupShow": "open"},
        ],
    },
]

columnDefs2 = [
    {
        "headerName": "GROUP A",
        "groupId": "groupA",
        "children": [
            {"field": "athlete"},
            {"field": "age"},
            {"field": "country", "columnGroupShow": "open"},
        ],
    },
    {
        "headerName": "Group B",
        "children": [
            {"field": "sport"},
            {"field": "year"},
            {"field": "date", "columnGroupShow": "open"},
        ],
    },
    {
        "headerName": "Group C",
        "groupId": "groupC",
        "children": [
            {"field": "total"},
            {"field": "gold", "columnGroupShow": "open"},
            {"field": "silver", "columnGroupShow": "open"},
            {"field": "bronze", "columnGroupShow": "open"},
            {"field": "extraA"},
            {"field": "extraB", "columnGroupShow": "open"},
        ],
    },
]

app.layout = html.Div(
    [
        html.Button("First Column Set", id="btn-column-group-set1"),
        html.Button("Second Column Set", id="btn-column-group-set2"),
        dag.AgGrid(
            id="updating-column-group2",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs1,
            defaultColDef={"sortable": True, "resizable": True, "filter": True},
            columnSize="autoSize",
        ),
    ]
)


@callback(
    Output("updating-column-group2", "columnDefs"),
    Input("btn-column-group-set1", "n_clicks"),
    Input("btn-column-group-set2", "n_clicks"),
    prevent_initial_call=True,
)
def toggle_cols(*_):
    return columnDefs2 if ctx.triggered_id == "btn-column-group-set2" else columnDefs1


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