plotly / dash-ag-grid

Dash AG Grid is a high-performance and highly customizable component that wraps AG Grid, designed for creating rich datagrids.
https://dash.plotly.com/dash-ag-grid
MIT License
175 stars 25 forks source link

Automatically select the first row #282

Closed brifordwylie closed 6 months ago

brifordwylie commented 6 months ago

So my use case is probably not that rare, you have a table of items at the top of the page and then details about those items in other components. So when the item table (ag-grid) gets populated you'd like the first row selected, and therefore a "selectedRows" event/callback gets invoked. That will populate the bottom components with the details of the selected row. Here's an example. The first row is selected 'automatically' and therefore populates the bottom components.

Screenshot 2023-12-29 at 8 38 42 PM

When using dash DataTable, this is done via the 'selected_rows' attribute...

 table = dash_table.DataTable(
            id=component_id,
            columns=[{"name": "Status", "id": "status"}],
            data=[{"status": "Waiting for data..."}],
            sort_action="native",
            row_selectable=row_select,
            cell_selectable=False,
            selected_rows=[0],

I'm not finding the equivalent in Ag-grid, my stack overflow/chat-gpt is failing me :)

Here's a reproducible example.. any suggestions are greatly appreciated...

import dash
import pandas as pd
from dash_ag_grid import AgGrid

app = dash.Dash(__name__)

def select_first_row_js():
    """Return the JavaScript to select the first row in the table"""
    return (
        "function(params) {"
        "  var firstRowNode = params.api.getDisplayedRowAtIndex(0);"
        "  if (firstRowNode) {"
        "    firstRowNode.setSelected(true);"
        "  }"
        "}"
    )

df = pd.DataFrame({
    "A": [1, 2, 3],
    "B": [4, 5, 6]
})

app.layout = AgGrid(
    id='my-table',
    columnSize="sizeToFit",
    dashGridOptions={
        "rowHeight": None,
        "domLayout": "normal",
        "rowSelection": "single",
        "filter": True,
        "firstDataRendered": select_first_row_js(),
    },
    style={"maxHeight": "200px", "overflow": "auto"},
    columnDefs=[{"headerName": col, "field": col, "filter": "agTextColumnFilter"} for col in df.columns],
    rowData=df.to_dict("records")
)

if __name__ == '__main__':
    app.run_server(debug=True)
AnnMarieW commented 6 months ago

Hi @brifordwylie

Try removing "firstDataRendered": select_first_row_js(), and using the selectedRows prop


app.layout = AgGrid(
    id='my-table',
    selectedRows=df.head(1).to_dict("records"),
    # other props
)

I'm not finding the equivalent in Ag-grid, my stack overflow/chat-gpt is failing me :)

Also, be sure to check out the Dash Community Forum :slightly_smiling_face:

brifordwylie commented 6 months ago

@AnnMarieW thanks for the reply, I'll look at the Forums... for the 'reproducible' I went too simple. the tables/components in our UI are dynamic, the data in them gets updated dynamically, new stuff comes, old stuff goes... but back to the topic.. I don't have the dataframe when the AgGrid is created... so it gets populated from a callback that returns 'columnDefs' and 'rowData'. I've tried adding a third output 'selectedRows' but that doesn't seem to work either... if it's better I'll close this ticket and move this to the Forums.

Here's a better 'reproducible'

import dash
from dash import html, dcc, Input, Output, State
import pandas as pd
from dash_ag_grid import AgGrid

app = dash.Dash(__name__)

# Placeholder DataFrame
df = pd.DataFrame()

app.layout = html.Div([
    html.Button('Update Table', id='update-button', n_clicks=0),
    AgGrid(
        id='my-table',
        columnSize="sizeToFit",
        dashGridOptions={
            "rowHeight": None,
            "domLayout": "normal",
            "rowSelection": "single",
            "filter": True,
        },
        style={"maxHeight": "200px", "overflow": "auto"},
    ),
    html.Div(id='selected-row-info')  # Div to display selected row information
])

@app.callback(
    Output('my-table', 'columnDefs'),
    Output('my-table', 'rowData'),
    Output('my-table', 'selectedRows'),
    Input('update-button', 'n_clicks'),
    prevent_initial_call=True
)
def update_data(n_clicks):
    df = pd.DataFrame({
        "A": [1, 2, 3],
        "B": [4, 5, 6]
    })
    column_defs = [{"headerName": col, "field": col, "filter": "agTextColumnFilter"} for col in df.columns]
    row_data = df.to_dict("records")
    selected_rows = df.head(1).to_dict("records")
    return column_defs, row_data, selected_rows

@app.callback(
    Output('selected-row-info', 'children'),
    Input('my-table', 'selectedRows')
)
def update_selected_row_info(selected_rows):
    selected_row_info = f"Selected Row: {selected_rows[0]}" if selected_rows else "No row selected"
    return selected_row_info

if __name__ == '__main__':
    app.run_server(debug=True)
AnnMarieW commented 6 months ago

Yes, this is a known issue in the latest release, so no need to post this to the forum. You can find some potential workarounds and follow the progress here https://github.com/plotly/dash-ag-grid/issues/274

brifordwylie commented 6 months ago

Okay, my Dash/AgGrid knowledge is a bit weak sauce... but looking at the workaround, there's a row_transaction and an async: False flag... so I've adjusted the reproducible example with those changes.. and still no luck, I'm sure it's pilot error :)

import dash
from dash import html, dcc, Input, Output, State
import pandas as pd
from dash_ag_grid import AgGrid

app = dash.Dash(__name__)

# Placeholder DataFrame
df = pd.DataFrame()

app.layout = html.Div([
    html.Button('Update Table', id='update-button', n_clicks=0),
    AgGrid(
        id='my-table',
        columnSize="sizeToFit",
        dashGridOptions={
            "rowHeight": None,
            "domLayout": "normal",
            "rowSelection": "single",
            "filter": True,
        },
        style={"maxHeight": "200px", "overflow": "auto"},
    ),
    html.Div(id='selected-row-info')  # Div to display selected row information
])

@app.callback(
    Output('my-table', 'columnDefs'),
    # Output('my-table', 'rowData'),
    Output('my-table', 'rowTransaction'),
    Output('my-table', 'selectedRows'),
    Input('update-button', 'n_clicks'),
    prevent_initial_call=True
)
def update_data(n_clicks):
    df = pd.DataFrame({
        "A": [1, 2, 3],
        "B": [4, 5, 6]
    })
    column_defs = [{"headerName": col, "field": col, "filter": "agTextColumnFilter"} for col in df.columns]
    row_data = df.to_dict("records")
    selected_rows = df.head(1).to_dict("records")
    # return column_defs, row_data, selected_rows
    return column_defs, {"add": df.to_dict("records"), "async": False}, selected_rows,

@app.callback(
    Output('selected-row-info', 'children'),
    Input('my-table', 'selectedRows')
)
def update_selected_row_info(selected_rows):
    selected_row_info = f"Selected Row: {selected_rows[0]}" if selected_rows else "No row selected"
    return selected_row_info

if __name__ == '__main__':
    app.run_server(debug=True)
brifordwylie commented 6 months ago

Also, just a quick thanks to the maintainers of this project. I know open source is sometimes a thankless job. I have a few projects myself. https://github.com/SuperCowPowers. Anyway, big fan, I see there's an Enterprise version for AGGrid, If I buy that does it support the maintainers for this project? I'm also using Bootstrap templates and components, would like to find a path to support the right folks.

AnnMarieW commented 6 months ago

Thank you for considering supporting the maintainers of open source projects!

Purchasing an AG Grid Enterprise license benefits not only the AG Grid team but also provides access to many additional features for your projects. AG Grid supports open source by releasing a feature-rich AG Grid Community version. While Plotly and its maintainers won't directly benefit from this purchase since we're not affiliated with AG Grid, there are several ways you can support us:

If you have the budget:

Other ways to support us:

I'm thrilled you mentioned dash-bootstrap-templates, as I maintain that library. While I don't have sponsorship set up, I'd be delighted if you could do any of the things listed above.

brifordwylie commented 6 months ago

@AnnMarieW Thanks for the suggestions, I'm going to try to do all of the above, but in particular would like to see @BSd3v put up a Sponsor link so that I can mash it :)