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