Dash ag grid how to selected row by using keyboard press up & down to focus cell

How to selected row by using keyboard press up & down focus cell?

For the cell clicked example, you can select a cell by mouse click,but can’t use keyboard press up & down to focus cell.

But in dash_table.DataTable,you can use keyboard press up & down to callback active cell then get cell informations.

So how to selected row by using keyboard press up & down to focus cell then callback cell information?

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

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/solar.csv")

app = Dash(__name__)

grid = dag.AgGrid(
    id="quickstart-grid",
    rowData=df.to_dict("records"),
    columnDefs=[{"field": i} for i in df.columns],
    defaultColDef={"resizable": True, "sortable": True, "filter": True, "minWidth":125},
    columnSize="sizeToFit",
    getRowId="params.data.State"
)

app.layout = html.Div([grid, html.Div(id="quickstart-output")])


@callback(
    Output("quickstart-output", "children"), Input("quickstart-grid", "cellClicked")
)
def display_cell_clicked_on(cell):
    if cell is None:
        return "Click on a cell"
    return f"clicked on cell value:  {cell['value']}, column:   {cell['colId']}, row index:   {cell['rowIndex']}"


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


It doesn’t appear to me that the dash wrapper of ag-grid responds to key events as of yet.

But - that doesn’t mean it isn’t possible. And a bit of a fun thing to explore. I must caution though, while this “works” I guess, I’m not so sure I can suggest it be used or at least relied upon. Consider it more a working example to sharpen if you want to live dangerously. But in a pinch, it can enable you to be able to select/deselect rows with the space bar.

I do a lot of … bad … things here to get it to work - including making use of the initial element render to get a JS function to run once - as well as passing off a keyboard event as a cellClick in order to get a notification back through to dash. Ugly stuff. Helpful? Maybe, or maybe not. But interesting!

from dash import (
    Dash,
    html,
    Input,
    Output,
    callback,
    clientside_callback,
    ClientsideFunction,
)
import dash_ag_grid as dag
import pandas as pd

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/solar.csv")

app = Dash(__name__)

grid = dag.AgGrid(
    id="quickstart-grid",
    rowData=df.to_dict("records"),
    columnDefs=[{"field": i} for i in df.columns],
    defaultColDef={
        "resizable": True,
        "sortable": True,
        "filter": True,
        "minWidth": 125,
    },
    columnSize="sizeToFit",
    getRowId="params.data.State",
)

app.layout = html.Div(
    [
        grid,
        html.Div(id="quickstart-output"),
    ]
)


@callback(
    Output("quickstart-output", "children"), Input("quickstart-grid", "cellClicked")
)
def display_cell_clicked_on(cell):
    if cell is None:
        return "Click on a cell"
    print(cell)
    return f"clicked on cell value:  {cell['value']}, column:   {cell['colId']}, row index:   {cell['rowIndex']}"


clientside_callback(
    ClientsideFunction(namespace="clientside", function_name="func"),
    Output("quickstart-output", "children", allow_duplicate=True),
    Input("quickstart-grid", "id"),
    prevent_initial_call="initial_duplicate",
)


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

"""
Put the following in a .js file in the assets folder (i called mine clientsideCallbacks.js)

-----------

window.dash_clientside = Object.assign({}, window.dash_clientside, {
    clientside: {
        func: onKeyPress
    }
});

async function onKeyPress() {
    let id = window.dash_clientside.callback_context.inputs_list[0]['id']

    const api = await window.dash_ag_grid.getApiAsync(id)
    
    api.addEventListener('cellKeyPress', (event) => {
        if (event.event.code == 'Space') {
            console.log('Spacebar hit')
            event.node.setSelected(!event.node.selected)

            api.gridOptionsService.gridOptions.onCellClicked(event)
        }

    })

    window.dash_clientside.no_update
}
"""

keypress_dash_ag_grid

Hello @RyanLin,

Welcome to the community!

@jbeyer, this is a lot of work for something that the grid already handles, which is the cellFocused event. :sweat_smile:

To attach, you perform a clientside callback like this:

app.clientside_callback(
    “””function (id) {
            dash_ag_grid.getApiAsync(id).then((grid) => grid.addEventListener(‘cellFocused’, console.log(…arguments)))
            return window.dash_clientside.no_update
        }
    “””, Output(grid, ‘id’), Input(grid,’id’)
)

The above code should print out the available context to the browser console.

1 Like

@jinnyzor I was wondering if i was missing something. It did seem like a lot of work …

okay so cellFocused might be a better event to listen to but then - how to get it back to Dash? Since we’re attaching it as an EventListener it’s not going to the output of our callback, so how would you make the event usable somehow? I was able to do that in my example by hijacking the onCellClicked event, but i don’t think you get the same type of event using cellFocused?

EDIT: Just saw your other post on using a button. that’s genius.

Enjoy:

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

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/solar.csv")

app = Dash(__name__)

grid = dag.AgGrid(
    id="quickstart-grid",
    rowData=df.to_dict("records"),
    columnDefs=[{"field": i} for i in df.columns],
    defaultColDef={"resizable": True, "sortable": True, "filter": True, "minWidth":125},
    columnSize="sizeToFit",
    getRowId="params.data.State"
)

app.layout = html.Div([grid, html.Div(id="quickstart-output")])


@callback(
    Output("quickstart-output", "children"), Input("quickstart-grid", "cellClicked")
)
def display_cell_clicked_on(cell):
    if cell is None:
        return "Click on a cell"
    return f"clicked on cell value:  {cell['value']}, column:   {cell['colId']}, row index:   {cell['rowIndex']}"

app.clientside_callback(
    """function (id) {
            dash_ag_grid.getApiAsync(id).then((grid) => grid.addEventListener("cellFocused", (event) => {
                document.querySelector(`#${id} div.ag-cell-focus`).click()
            }))
            return window.dash_clientside.no_update
        }
    """, Output('quickstart-grid', 'id'), Input('quickstart-grid','id')
)

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

@jbeyer

3 Likes

@jinnyzor Thank you, this way resolved my issue.

1 Like