Dash ag grid: deselect the ag grid checkboxes using a callback

Hai,

Congratulation for the great work!
I have created a tree data structure with checkboxes with dash ag grid. When the user selects the checkbox corresponding row data will be displayed as a dbc.Button component. So, would there be any possibility to deselect an individual checkbox by clicking the corresponding button?

And another question would be, can you tell me how can I create the icons as shown on (this page)?

Thanks

1 Like

Hello @Raghava,

Not sure what you mean by the button, can you provide an MRE?

As far as rendering, you can follow along with their example and generate a custom cellRenderer component for the files:

function getFileCellRenderer() {
  class FileCellRenderer {
    init(params) {
      var tempDiv = document.createElement('div');
      var value = params.value;
      var icon = getFileIcon(params.value);
      tempDiv.innerHTML = icon
        ? '<span><i class="' +
          icon +
          '"></i>' +
          '<span class="filename"></span>' +
          value +
          '</span>'
        : value;
      this.eGui = tempDiv.firstChild;
    }
    getGui() {
      return this.eGui;
    }
    refresh() {
      return false;
    }
  }
  return FileCellRenderer;
}
function getFileIcon(name) {
  return endsWith(name, '.mp3') || endsWith(name, '.wav')
    ? 'far fa-file-audio'
    : endsWith(name, '.xls')
    ? 'far fa-file-excel'
    : endsWith(name, '.txt')
    ? 'far fa-file'
    : endsWith(name, '.pdf')
    ? 'far fa-file-pdf'
    : 'far fa-folder';
}
function endsWith(str, match) {
  var len;
  if (str == null || !str.length || match == null || !match.length) {
    return false;
  }
  len = str.length;
  return str.substring(len - match.length, len) === match;
}
1 Like

@jinnyzor

Thanks for the reply.

I kept my code for your reference. In this example, as I mentioned before when the user clicks the checkbox of the file, a html.Li component containing a dbc.Button will be displayed. So, my question would be, can the checkbox be deselected when the user clicks the displayed button?

import os
import csv
import dash_ag_grid as dag
from dash import Dash, Input, Output, html, dcc, State
import dash_bootstrap_components as dbc

rowData = [
    {
        "filepath": ['Documents'],
        "size": 20,
    },
    {
        "filepath": ['Documents', 'txt'],
    },
    {
        "filepath": ['Documents', 'txt', 'notes.txt'],
        "dateModified": 'May 21 2017 01:50:00 PM',
        "size": 14.7,
    },
    {
        "filepath": ['Documents', 'pdf'],
    },
    {
        "filepath": ['Documents', 'pdf', 'book.pdf'],
        "dateModified": 'May 20 2017 01:50:00 PM',
        "size": 2.1,
    },
    {
        "filepath": ['Documents', 'pdf', 'cv.pdf'],
        "dateModified": 'May 20 2016 11:50:00 PM',
        "size": 2.4,
    },
    {
        "filepath": ['Documents', 'xls'],
    },
    {
        "filepath": ['Documents', 'xls', 'accounts.xls'],
        "dateModified": 'Aug 12 2016 10:50:00 AM',
        "size": 4.3,
    },
    {
        "filepath": ['Documents', 'stuff'],
    },
    {
        "filepath": ['Documents', 'newfolder'],
    },
    {
        "filepath": ['Documents', 'stuff', 'xyz.txt'],
        "dateModified": 'Jan 17 2016 08:03:00 PM',
        "size": 1.1,
    },
    {
        "filepath": ['Music', 'mp3', 'pop'],
        "dateModified": 'Sep 11 2016 08:03:00 PM',
        "size": 14.3,
    },
    {
        "filepath": ['temp.txt'],
        "dateModified": 'Aug 12 2016 10:50:00 PM',
        "size": 101,
    },
    {
        "filepath": ['Music', 'mp3', 'pop', 'theme.mp3'],
        "dateModified": 'Aug 12 2016 10:50:00 PM',
        "size": 101,
    },
    {
        "filepath": ['Music', 'mp3', 'jazz'],
        "dateModified": 'Aug 12 2016 10:50:00 PM',
        "size": 101,
    },
]



app = Dash(__name__)


def create_html_path_list(entries):
    return [
        dbc.Row([
            html.Li([
                dbc.Button(
                    "X",
                    n_clicks=0,
                    key=[name],
                    style={"width": "20px", "height": "20px",
                           "padding": "0px", "margin-right": "5px"},
                    class_name="btn btn-danger btn-sm",
                    id={"type": "remove_file", "index": name}
                ),
                html.Span(name)
            ]),
        ],
            style={"flexWrap": "nowrap"}
        ) for name in entries
    ]

grid = html.Div(
    [
        dag.AgGrid(
            id="grid",
            className="ag-theme-balham-dark",
            columnDefs=[
                # we're using the auto group column by default!
                {"field": "dateModified"},

            ],
            defaultColDef={
                "flex": 1,
                "sortable": True,
                "filter": True,
            },
            dashGridOptions={
                "autoGroupColumnDef": {
                    "headerName": "Drive",
                    "minWidth": 150,
                    # "cellRenderer": {"function": "getFileCellRenderer()"},
                    "cellRendererParams": {
                        "suppressCount": True,
                        "checkbox": True,
                    },
                },
                "groupDefaultExpanded": 1,
                "getDataPath": {"function": "getDataPath(params)"},
                "treeData": True,
                "animateRows": True,
                "rowSelection": 'multiple',
                "groupSelectsChildren": True,
                "suppressRowClickSelection": True,
                # "suppressAggFilteredOnly": True,
                # "groupAggFiltering": True,
                # no blue highlight
                "suppressRowHoverHighlight": True,
            },
            rowData=rowData,
            enableEnterpriseModules=True,
        ),
    ]
)

app.layout = html.Div(
    [
        dcc.Markdown("Example: Tree Data "),
        dcc.Input(id="just-filtered-input",
                  placeholder="filter...",
                  style={"width": "400px"}),
        html.Br(), html.Br(),
        grid,
        html.Div(id="output"),
    ],
    style={"width": 1000}
)


@app.callback(
    Output("output", "children"),
    Input("grid", "selectedRows"),
    Input("grid", "cellClicked"),
)
def selected(selected_rows, cellClicked):
    # print(cellClicked)
    if selected_rows:
        original_paths = []
        selected_paths = [s["filepath"] for s in selected_rows]
        for path_parts in selected_paths:
            new_path = "/".join(path_parts)
            original_paths.append(new_path)
        return create_html_path_list(original_paths)
    return "No selections"


@app.callback(
    Output("grid", "dashGridOptions"),
    Input("just-filtered-input", "value"),
    State("grid", "dashGridOptions"),
)
def update_filter(filter_value, gridOptions):
    gridOptions["quickFilterText"] = filter_value
    return gridOptions


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

Sure, here is an example of using patch to delete the items in the list:

import os
import csv
import dash_ag_grid as dag
from dash import Dash, Input, Output, html, dcc, State, ALL, ctx, Patch, no_update
import dash_bootstrap_components as dbc

rowData = [
    {
        "filepath": ['Documents'],
        "size": 20,
    },
    {
        "filepath": ['Documents', 'txt'],
    },
    {
        "filepath": ['Documents', 'txt', 'notes.txt'],
        "dateModified": 'May 21 2017 01:50:00 PM',
        "size": 14.7,
    },
    {
        "filepath": ['Documents', 'pdf'],
    },
    {
        "filepath": ['Documents', 'pdf', 'book.pdf'],
        "dateModified": 'May 20 2017 01:50:00 PM',
        "size": 2.1,
    },
    {
        "filepath": ['Documents', 'pdf', 'cv.pdf'],
        "dateModified": 'May 20 2016 11:50:00 PM',
        "size": 2.4,
    },
    {
        "filepath": ['Documents', 'xls'],
    },
    {
        "filepath": ['Documents', 'xls', 'accounts.xls'],
        "dateModified": 'Aug 12 2016 10:50:00 AM',
        "size": 4.3,
    },
    {
        "filepath": ['Documents', 'stuff'],
    },
    {
        "filepath": ['Documents', 'newfolder'],
    },
    {
        "filepath": ['Documents', 'stuff', 'xyz.txt'],
        "dateModified": 'Jan 17 2016 08:03:00 PM',
        "size": 1.1,
    },
    {
        "filepath": ['Music', 'mp3', 'pop'],
        "dateModified": 'Sep 11 2016 08:03:00 PM',
        "size": 14.3,
    },
    {
        "filepath": ['temp.txt'],
        "dateModified": 'Aug 12 2016 10:50:00 PM',
        "size": 101,
    },
    {
        "filepath": ['Music', 'mp3', 'pop', 'theme.mp3'],
        "dateModified": 'Aug 12 2016 10:50:00 PM',
        "size": 101,
    },
    {
        "filepath": ['Music', 'mp3', 'jazz'],
        "dateModified": 'Aug 12 2016 10:50:00 PM',
        "size": 101,
    },
]



app = Dash(__name__)


def create_html_path_list(entries):
    return [
        dbc.Row([
            html.Li([
                dbc.Button(
                    "X",
                    n_clicks=0,
                    key=[name],
                    style={"width": "20px", "height": "20px",
                           "padding": "0px", "margin-right": "5px"},
                    class_name="btn btn-danger btn-sm",
                    id={"type": "remove_file", "index": name}
                ),
                html.Span(name)
            ]),
        ],
            style={"flexWrap": "nowrap"}
        ) for name in entries
    ]

grid = html.Div(
    [
        dag.AgGrid(
            id="grid",
            className="ag-theme-balham-dark",
            columnDefs=[
                # we're using the auto group column by default!
                {"field": "dateModified"},

            ],
            defaultColDef={
                "flex": 1,
                "sortable": True,
                "filter": True,
            },
            dashGridOptions={
                "autoGroupColumnDef": {
                    "headerName": "Drive",
                    "minWidth": 150,
                    # "cellRenderer": {"function": "getFileCellRenderer()"},
                    "cellRendererParams": {
                        "suppressCount": True,
                        "checkbox": True,
                    },
                },
                "groupDefaultExpanded": 1,
                "getDataPath": {"function": "getDataPath(params)"},
                "treeData": True,
                "animateRows": True,
                "rowSelection": 'multiple',
                "groupSelectsChildren": True,
                "suppressRowClickSelection": True,
                # "suppressAggFilteredOnly": True,
                # "groupAggFiltering": True,
                # no blue highlight
                "suppressRowHoverHighlight": True,
            },
            rowData=rowData,
            enableEnterpriseModules=True,
        ),
    ]
)

app.layout = html.Div(
    [
        dcc.Markdown("Example: Tree Data "),
        dcc.Input(id="just-filtered-input",
                  placeholder="filter...",
                  style={"width": "400px"}),
        html.Br(), html.Br(),
        grid,
        html.Div(id="output"),
    ],
    style={"width": 1000}
)


@app.callback(
    Output("output", "children"),
    Input("grid", "selectedRows"),
    Input("grid", "cellClicked"),
)
def selected(selected_rows, cellClicked):
    # print(cellClicked)
    if selected_rows:
        original_paths = []
        selected_paths = [s["filepath"] for s in selected_rows]
        for path_parts in selected_paths:
            new_path = "/".join(path_parts)
            original_paths.append(new_path)
        return create_html_path_list(original_paths)
    return "No selections"


@app.callback(
    Output("grid", "dashGridOptions"),
    Input("just-filtered-input", "value"),
    State("grid", "dashGridOptions"),
)
def update_filter(filter_value, gridOptions):
    gridOptions["quickFilterText"] = filter_value
    return gridOptions

@app.callback(
    Output("grid", "selectedRows"),
    Input({"type": "remove_file", "index": ALL}, "n_clicks"),
    State("grid", "selectedRows"),
    prevent_initial_call=True
)
def deselect(_, s):
    if sum(_) > 0:
        selects = Patch()
        for x in range(len(s)-1, -1, -1):
            if all(i in ctx.triggered_id.index.split('/') for i in s[x]['filepath'] ):
                del selects[x]
        return selects
    return no_update


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

Future reference, please provide the JS to make your stuff work as well. :wink:

Also, for when trying to remove from a list, always work in reverse order, otherwise you will just give yourself grief, haha.

2 Likes

@jinnyzor

Thanks a lot. That’s exactly what I wanted.

1 Like