Unwanted tab persistence

I have a table with 4 rows. The user is able to select as many rows as they want and when they press continue, a tab for each selected row is visible.

Use case:
A user selects a row and presses continue, a tab is visible. They select a second row from the old table and press continue again. This time 2 tabs are seen but the old selection is persistent. Is there a way to disable this?

The code looks like this:

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

import pandas as pd
from dash.exceptions import PreventUpdate
import dash_ag_grid as dag


app = dash.Dash(__name__)

app.layout = html.Div([
    html.Button("Table", id='get_table'),
    html.Div(id='table_div'),
    html.Button('Continue', id='continue'),
    dcc.Tabs(id = "tabs", persistence=False),
    html.Div(id="grid_div"),
    dcc.Store(id="selected"), 
    dcc.Store(id = 'df_dict'),
    dcc.Store(id= 'dict'),
])


@app.callback(
    Output('table_div', 'children'),
    Output('df_dict', 'data'),
    Input('get_table', 'n_clicks'),
    prevent_initial_call=True
)
def read_table(n_clicks):
    if n_clicks:
        
        df = pd.read_csv('table.csv')
        dfm = df[["sn","name","path"]]
        full_df = df.to_dict()
        
        
        column_defs = []
        for col_index, col_name in enumerate(dfm.columns):
            column_defs.append(
                {
                    "headerName": f"{col_name}",
                    "field": f"{col_name}",
                    "width": 500,
                    "resizable": False,
                    "checkboxSelection": True if col_index == 0 else False,
                    "filter": True,
                }
            )

        grid = dag.AgGrid(
            id="grid",
            className="ag-theme-alpine border",
            rowData=dfm.to_dict("records"),
            columnDefs=column_defs,
            columnSize="sizeToFit",
            #selectedRows=overlapping,
            dashGridOptions={
                "domLayout": "autoHeight",
                "columnHoverHighlight": True,
                "rowSelection": "multiple",
            },
            style={"height": "100%", "fontSize": "20px"},
        )
        return grid, full_df
    

@app.callback(
    Output("selected", "data", allow_duplicate=True),
    Input("grid", "selectedRows"),
    prevent_initial_call=True,
)
def get_user_selections(selected):

    if not selected:
        raise PreventUpdate
    
    print(selected)
    return selected

   
@app.callback(
    Output("tabs", "style"),
    Output("tabs", "children"),
    Output("dict", "data"),
    Input("continue", "n_clicks"),
    State("selected", "data"),
    State("df_dict", "data"),
    prevent_initial_call=True,
)
def get_tabs(n_clicks, selected, df):
    tab_names = [] 
    full_path = []
    print(f"df{df}")
    print(f"selected{selected}")
    for rows in selected:
        if 'sn' in rows and 'path' in rows:
            tab_names.append(f"{rows['path']} - {rows['sn']}")

            for key, value in df['sn'].items():
                if value == rows['sn'] and df['path'].get(key) == rows['path']:
                    full_path.append(df['full_path'].get(key))

    print(f"tabs:{tab_names}")

    n_tabs = len(tab_names)
    tabs = [dcc.Tab(label=tab_names[0], value= tab_names[0])]
    if n_tabs > 1:
        for i in range(2,n_tabs+1):
            tabs = tabs + [dcc.Tab(label=tab_names[i-1], value= tab_names[i-1])]
    
    selected_dict = []
    
    for index in range(n_tabs):
        selected_dict.append({tab_names[index]: {"fa": full_path[index] , "ft": None}})

    print(f"selected:{selected}")
        
    return dict(), tabs, selected_dict


@app.callback(
    Output("grid_div", "children"),
    Input("tabs", "value"),
    State("dict", "data"),
    prevent_initial_call=True,
)
def get_grids(value, existing):
    if value:
        df = pd.read_csv(f"{value}.csv")

    column_defs = []
    for col_index, col_name in enumerate(df.columns):
        column_defs.append(
            {
                "headerName": f"{col_name}",
                "field": f"{col_name}",
                "width": 500,
                "resizable": False,
                "checkboxSelection": True if col_index == 0 else False,
                "filter": True,
            }
        )

    
    overlapping = []
    rowData=df.to_dict("records")

    print(f"existing:{existing}")
    print(f'row:{rowData}')

    for item in existing:
        if item[value]["ft"] in rowData:
            overlapping.append(item[value]["ft"])



    grid = dag.AgGrid(
        id="table",
        rowData=df.to_dict("records"),
        columnDefs=column_defs,
        columnSize="sizeToFit",
        selectedRows=overlapping,
        dashGridOptions={
            "domLayout": "autoHeight",
            "columnHoverHighlight": True,
            "rowSelection": "multiple",
        },
        style={"height": "100%", "fontSize": "20px"},
    )
    return grid


@app.callback(
    Output("dict", "data", allow_duplicate=True),
    Input("table","selectedRows"),
    State('tabs', 'value'),
    State("dict", "data"),
    prevent_initial_call=True
)

def store(selectedRows, dropdown_value, data):
    for items in data:
        items[dropdown_value]["ft"]=selectedRows
        print(f"data{data}")
        return data


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

and the csv file with the table:

sn,name,path,full_path

123,a,1,fp1

123,a,2,fp2

345,c,1,fp3

345,c,3,fp4

Hey @subodhpokhrel7, the code snippet you posted throws errors. Please have a read here:

Hi @AIMPED ,

sorry about that. It should be fine now.

Although there are some errors visible on the right, they don’t have any effect on the functionality as far as the question is concerned.

Unfortunately I don’t understand what you want to achieve. I understood that you are creating tabs dynamically.

example

As you can see here, when I create and select tab 1-123 and then go back and create more tabs, 1-123 is still selected. I want the tabs to be shown with none selected

You want to reset the selection once the continue button has been clicked? Take a look at the second callback.

import dash_ag_grid as dag
from dash import Dash, html, dcc, 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": "athlete", "checkboxSelection": True, "headerCheckboxSelection": True},
    {"field": "age", "maxWidth": 100},
    {"field": "country"},
    {"field": "year", "maxWidth": 120},
    {"field": "date", "minWidth": 150},
    {"field": "sport"},
    {"field": "gold"},
    {"field": "silver"},
    {"field": "bronze"},
    {"field": "total"},
]

defaultColDef = {
    "flex": 1,
    "minWidth": 150,
    "sortable": True,
    "resizable": True,
    "filter": True,
}

app.layout = html.Div(
    [
        dcc.Markdown("This grid has multi-select rows with checkboxes."),
        html.Button(id='reset', children='reset'),
        dag.AgGrid(
            id="selection-checkbox-grid",
            columnDefs=columnDefs,
            rowData=df.to_dict("records"),
            defaultColDef=defaultColDef,
            dashGridOptions={"rowSelection": "multiple"},
        ),
        html.Div(id="selections-checkbox-output"),
    ],
    style={"margin": 20},
)


@callback(
    Output("selections-checkbox-output", "children"),
    Input("selection-checkbox-grid", "selectedRows"),
)
def selected(selected):
    if selected:
        selected_athlete = [s["athlete"] for s in selected]
        return f"You selected athletes: {selected_athlete}"
    return "No selections"


@callback(
    Output("selection-checkbox-grid", "selectedRows"),
    Input("reset", "n_clicks"),
    prevent_initial_call=True
)
def selected(_):
    return None


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

Not really.
I want the selected tab to reset when the tabs change.
The idea would be the same if dcc.Tabs had the ‘selected’ property, but it doesn’t.

I surrender- almost.

The dash-bootstrap-comopnents Tabs have a property active_tab, maybe this is what you are after.

But I’m still not sure…

https://dash-bootstrap-components.opensource.faculty.ai/docs/components/tabs/

1 Like

Just checked it.

Sending None as output to active_cell solves the problem.

@callback(
    Output("tab_id", "active_tab"),
    Input(),
)
def clear():

    return None

Since it was so simple, I went back to dcc.Tabs and yet again, sending None as value does the job.

@callback(
    Output("tab_id", "value"),
    Input(),
)
def clear():

    return None

1 Like