Maintaining state of datatable while switching tab

Hi All,

I am having a two tab dash webapp. In the first tab, there is a scrollable datatable and there is a callback triggered when clicking a particular cell of a row which brings user to the other tab. Now once we go back to the first tab, the state of datatable is set back to initial and we need to scroll again to find the cell in the datatable.
Is there any way we can freeze the state of datatable to the state where we click on the cell?

hi @anuj_tiwari

I’m not sure I completely get what you mean by state of the datatable, but perhaps the persistence or persisted_props properties will help you achieve what you want. See the very bottom of our DataTable reference page.

Hi @adamschroeder ,

Thank you for reply!
Let me explain with a sample webapp:

import plotly.express as px
import dataiku
import dash_bootstrap_components as dbc
import dash
import logging
import datetime
import pandas as pd
from dash.dependencies import Input, Output, State
from dash import dcc, dash_table,callback, html #, Input, Output
import dash_bootstrap_components as dbc
from dash.exceptions import PreventUpdate
import datetime
import numpy as np
from flask import request
import os
import json
import plotly.graph_objects as go
from collections import OrderedDict

app = dash.Dash(__name__)



app.layout = html.Div([
    dcc.Tabs(id="tabs-group", value='list1', children=[

            dcc.Tab(label='first', value='list1'),
            dcc.Tab(label='second', value='list2'),
            dcc.Tab(label='tips', value='tips')

        ],
        style= { 'width':'300px','height':'50px'}),
       html.Div(id='tabs-content')

])

def build_content1():
    df = pd.DataFrame(OrderedDict([
    ('climate', ['Sunny', 'Snowy', 'Sunny', 'Rainy','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o']),
    ('temperature', [13, 43, 50, 30,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]),
    ('city', ['NYC', 'Montreal', 'Miami', 'NYC','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o'])
]))
    cont = html.Div([
        html.H5("datatable", style={"display": "inline-block","marginLeft":"10px"}),
        dash_table.DataTable(
        id='table-dropdown1',
        data=df.to_dict('records'),     #the contents of the table
        columns=[
            {'id': 'feedback', 'name': 'feedback', 'presentation':'input'},
            {'id': 'climate', 'name': 'climate', 'presentation': 'dropdown'},
            {'id': 'temperature', 'name': 'temperature'},
            {'id': 'city', 'name': 'city', 'presentation': 'dropdown'},
        ],
        editable=True,
persistence = True,
                is_focused = True,
                persisted_props=["page_current",'data'],
                filter_action="native",
                sort_action="native",
                sort_mode="multi",
                page_size= 200,
            style_header={
                'fontWeight': 'bold',
                     'height': 'auto', 'whiteSpace': 'normal',
                        'backgroundColor': '#FFF8DC', 
                    
                    'position': 'sticky', 'top':'-2px', 
                    'zIndex' : 300,
                    'height': '100px'
                   
                },
            style_data = { 'height': 'auto','whiteSpace': 'normal'},
                style_table ={
                    'height': '400px', 'overflowY': 'auto', 'overflowX': 'auto',
                    'paddingTop': '0px'
                },

        dropdown={                      #dictionary of keys that represent column IDs,
            'climate': {                #its values are 'options' and 'clearable'
                'options': [            #'options' represents all rows' data under that column
                    {'label': i, 'value': i}
                    for i in df['climate'].unique()
                ],

                'clearable':True
            },
            'city': {
                'options':[
                    {'label': 'NYC', 'value': 'NYC'},
                    {'label': 'Miami', 'value': 'Miami'},
                    {'label': 'Montreal', 'value': 'Montreal'}
                ],

                'clearable':False
            }
        }
    )
    ])
    return cont
    
def build_content2():
    df = pd.DataFrame(OrderedDict([
    ('climate', ['Sunny', 'Snowy', 'Sunny', 'Rainy']),
    ('temperature', [13, 43, 50, 30]),
    ('city', ['NYC', 'Montreal', 'Miami', 'NYC'])
]))
    cont = html.Div([
        dash_table.DataTable(
        id='table-dropdown2',
        data=df.to_dict('records'),     #the contents of the table
        columns=[
            {'id': 'feedback', 'name': 'feedback', 'presentation':'input'},
            {'id': 'climate', 'name': 'climate', 'presentation': 'dropdown'},
            {'id': 'temperature', 'name': 'temperature'},
            {'id': 'city', 'name': 'city', 'presentation': 'dropdown'},
        ],
        editable=True,

        dropdown={                      #dictionary of keys that represent column IDs,
            'climate': {                #its values are 'options' and 'clearable'
                'options': [            #'options' represents all rows' data under that column
                    {'label': i, 'value': i}
                    for i in df['climate'].unique()
                ],

                'clearable':True
            },
            'city': {
                'options':[
                    {'label': 'NYC', 'value': 'NYC'},
                    {'label': 'Miami', 'value': 'Miami'},
                    {'label': 'Montreal', 'value': 'Montreal'}
                ],

                'clearable':False
            }
        }
    )
                
    ])
    return cont
    
def build_tips_tricks():
    cont = html.Div([
        html.Br(), html.Br(),
        html.H4(
            "1."),
        html.H4(
            "2. "),
        html.H4("3.	."),
        html.H4(
            "4..")

    ])
    return cont
@app.callback(
    Output('tabs-content', 'children'),
    Output('tabs-group', 'value'),
    Input('tabs-group', 'value')
)
def render_content(tab):
    if tab == 'list1':
        cont = build_content1()
        return  cont,tab
    elif tab == 'tips':
        cont = build_tips_tricks()
        return cont, tab
    elif tab == 'list2':
        cont = build_content2()
        return cont, tab    
    
if __name__ == '__main__':
    app.run_server(debug=True)

On the “first” tab When I select a sample temperature while scrolling down to 12, go to ‘second’ tab and revisit ‘first’ tab, then I have lost the state of datatable i.e if I need to check the row with temperature 12, then I have to scroll down again.
Is there any way we can freeze the ‘first tab’ when we click a particular cell and change tab?

hi @anuj_tiwari ,
I don’t think that is possible with Tabs. However, there should be a workaround with Div’s hidden prop. Instead of creating tabs, you can in theory create buttons (that look like tabs) that will switch between hidden=True hidden=False whenever clicked on. That will preserve the state of the DataTable.

So technically, you will have all three layouts (your original tab content) on the page, two of which will be hidden=True, and one hidden=False. You can use callback context to unhide layout B while hiding the other layouts.

from dash import Dash, dcc, html, Output, Input, ctx, dash_table, callback
import dash_bootstrap_components as dbc
from collections import OrderedDict
import pandas as pd

df = pd.DataFrame(OrderedDict([
('climate', ['Sunny', 'Snowy', 'Sunny', 'Rainy','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o']),
('temperature', [13, 43, 50, 30,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]),
('city', ['NYC', 'Montreal', 'Miami', 'NYC','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o'])
]))


app = Dash(__name__,external_stylesheets=[dbc.themes.CYBORG])

contentA = html.Div(
    [
        dbc.Row(
            [
                dbc.Col([
                    dash_table.DataTable(
                        id='table-dropdown1',
                        data=df.to_dict('records'),
                        # the contents of the table
                        columns=[
                            {'id': 'feedback', 'name': 'feedback',
                             'presentation': 'input'},
                            {'id': 'climate', 'name': 'climate',
                             'presentation': 'dropdown'},
                            {'id': 'temperature', 'name': 'temperature'},
                            {'id': 'city', 'name': 'city',
                             'presentation': 'dropdown'},
                        ],
                        editable=True,
                        persistence=True,
                        is_focused=True,
                        persisted_props=["page_current", 'data'],
                        filter_action="native",
                        sort_action="native",
                        sort_mode="multi",
                        page_size=200,
                        style_header={
                            'fontWeight': 'bold',
                            'height': 'auto', 'whiteSpace': 'normal',
                            'backgroundColor': '#FFF8DC',

                            'position': 'sticky', 'top': '-2px',
                            'zIndex': 300,
                            'height': '100px'

                        },
                        style_data={'height': 'auto',
                                    'whiteSpace': 'normal'},
                        style_table={
                            'height': '400px', 'overflowY': 'auto',
                            'overflowX': 'auto',
                            'paddingTop': '0px'
                        },

                        dropdown={
                            # dictionary of keys that represent column IDs,
                            'climate': {
                                # its values are 'options' and 'clearable'
                                'options': [
                                    # 'options' represents all rows' data under that column
                                    {'label': i, 'value': i}
                                    for i in df['climate'].unique()
                                ],

                                'clearable': True
                            },
                            'city': {
                                'options': [
                                    {'label': 'NYC', 'value': 'NYC'},
                                    {'label': 'Miami', 'value': 'Miami'},
                                    {'label': 'Montreal',
                                     'value': 'Montreal'}
                                ],

                                'clearable': False
                            }
                        }
                    )
                ])

            ]),
    ], hidden=False, id='A'

)

contentB = html.Div(
    [
        dbc.Row(
            [
                dbc.Col(html.H6("""This is the content for Layout B which has a lot of cool stuff""")),

            ]),
    ], hidden=True, id='B'

)

contentC = html.Div(
    [
        dbc.Row(
            [
                dbc.Col(html.H6("""Content C: come and learn all that you can do with content C.""")),

            ]),
    ], hidden=True, id='C'

)

app.layout = dbc.Container([
    dbc.Row([
       dbc.Col([dbc.Button("TabA", id='btn1')], width=4),
       dbc.Col([dbc.Button("TabB", id='btn2')], width=4),
       dbc.Col([dbc.Button("TabC", id='btn3')], width=4)
    ]),
    contentA, contentB, contentC

])


@callback(
    Output('A', 'hidden'),
    Output('B', 'hidden'),
    Output('C', 'hidden'),
    Input('btn1', 'n_clicks'),
    Input('btn2', 'n_clicks'),
    Input('btn3', 'n_clicks')
)
def update_content_shown(b1,b2,b3):
    if ctx.triggered_id == 'btn1':
        return False, True, True
    elif ctx.triggered_id == 'btn2':
        return True, False, True
    elif ctx.triggered_id == 'btn3':
        return True, True, False
    else:
        return False, True, True




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

Thank you @adamschroeder for the workaround!

@adamschroeder hey Adam, the webapp is huge and there are lots of callbacks triggered from the 2nd tab and trying to convert this to buttons is just falling apart. Just checking if there is any other way? Please advise.

hi @anuj_tiwari
how about converting this into a multipage app with Dash Pages?