Absolutely Brain-Numbing Callback Issue with Dash

Hey Guys, been learning Dash for 3 days now non stop. Just wanted to build a simple application to then record as GIF/Lotto animation to put on the landing page. But this simple example does not work for some reason.

All i want is a simple callback from the button thats it, there are no bugs, nothing on console and for some odd reason nothing prints on callbacks aswell. Tried all online suggested stuff and chat gpt’d the hell outta this, no luck. Back to consulting you pros.

Python Version = 3.11
Plotly version = 5.15.0
Dash Version = 2.11.1

What i tried: Built a simple MRE which worked

from dash import Dash, html, Input, Output

app = Dash(__name__)

app.layout = html.Div([
    html.Button('Search', id='button-render'),
    html.Div(id='terminal-text-action'),
    html.Div(id='table-container'),
])

@app.callback(
    [Output('terminal-text-action', 'children'),
     Output('table-container', 'children')],
    Input('button-render', 'n_clicks'),
    prevent_initial_call=True
)
def update_content(n_clicks):
    terminal_text = ""
    table = ""
    if n_clicks == 1:
        terminal_text = "First click message."
        table = html.Table([
            html.Tr([html.Th("Rate of Change"), html.Th("QT")]),
            html.Tr([html.Td("12"), html.Td("Q1-22")]),
        ])
    elif n_clicks >= 2:
        terminal_text = "Second click message."
        table = html.Table([
            html.Tr([html.Th("LOB"), html.Th("%")]),
            html.Tr([html.Td("lob-1"), html.Td("12%")]),
        ])

    return terminal_text, table

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

But the main app just doesnt (Latest dash and python):

Main script (app.py)

from dash import dcc, html, Dash
from dash.dependencies import Input, Output
import page1
import page2

app = Dash(__name__)

app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content')
])

@app.callback(
    Output('page-content', 'children'),
    [Input('url', 'pathname')]
)
def display_page(pathname):
    if pathname == '/page1':
        return page1.layout
    elif pathname == '/page2':
        return page2.layout
    else:
        return '404 Page Not Found'

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

Page 1 (This is where the bug is):

from dash import html, dcc
from navbar import Navbar
import pandas as pd
from dash import dash_table
from dash import Output, Input, State
from app import app
from dash.exceptions import PreventUpdate
import pandas as pd

def create_first_table():
    data = {
        'Rate of Change': [12, 14, 15, 15, 16, 15, 16, 16],
        'QT': ['Q1-22', 'Q2-22', 'Q3-22', 'Q4-22', 'Q1-23', 'Q2-23', 'Q3-23', 'Q4-23']
    }
    df = pd.DataFrame(data)
    return dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])

def create_second_table():
    data = {
        'LOB': ['lob-1', 'lob-2', 'lob-3', 'lob-4'],
        '%': ['12%', '14%', '33%', '46%']
    }
    df = pd.DataFrame(data)
    return dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])


# Read the CSV file
mom_data = pd.read_csv('MOM_data.csv')

@app.callback(
    [Output('terminal-text-action', 'children'),
     Output('table-container', 'children')],
    Input('button-render', 'n_clicks'),
    prevent_initial_call=True  # Prevents the callback from being triggered on page load
)
def update_content(n_clicks):
    print(f"Callback triggered with n_clicks: {n_clicks}")  # Debug print
    if n_clicks == 1:
        terminal_text = "It looks like there was a minor reduction in claims being filed manually, further more progress looks to have stagnated in 2023. Want me to dive deep into it?"
        table = create_first_table()
    elif n_clicks >= 2:  # Corrected condition
        terminal_text = "Looks like Lob-1 and Lob 2 received the lowest % of auto claims compared to rest."
        table = create_second_table()
    else:
        raise PreventUpdate

    return terminal_text, table



layout = html.Div([
    Navbar(),

    # Search Box and Button
    html.Div([
        dcc.Input(type='text', value='', placeholder='Enter Query Here', className='query-input'),
        html.Button('Search', id='button-render')
    ], className='search-box'),

    # Terminal Text Div
    html.Div(id='terminal-text-action', className='terminal-text'),

    # Table Div
    html.Div(id='table-container'),
    # Dashboard Block
    html.Div([
        html.H2('CRP SEGMENT', className='dashboard-title'),
        # KPI Blocks
        html.Div([
            # KPI Block 1: Overall Claims
            html.Div([
                html.H3('Overall Claims'),
                html.Div([
                    html.Div([
                        html.Div('2022', className='kpi-year'),
                        html.Div('118.7M', className='kpi-value')
                    ], className='kpi-segment'),
                    html.Div([
                        html.Div('2023', className='kpi-year'),
                        html.Div('97.56M', className='kpi-value')
                    ], className='kpi-segment')
                ], className='kpi-row'),
            ], className='kpi-block'),
            # KPI Block 2: Defect Volume
            html.Div([
                html.H3('Defect Volume'),
                html.Div([
                    html.Div([
                        html.Div('2022', className='kpi-year'),
                        html.Div('4.39M', className='kpi-value')
                    ], className='kpi-segment'),
                    html.Div([
                        html.Div('2023', className='kpi-year'),
                        html.Div('3.30M', className='kpi-value')
                    ], className='kpi-segment')
                ], className='kpi-row'),
            ], className='kpi-block'),
            # KPI Block 3: FSA %
            html.Div([
                html.H3('FSA %'),
                html.Table([
                    html.Thead([
                        html.Tr([
                            html.Th('Type'),
                            html.Th('2023')
                        ])
                    ]),
                    html.Tbody([
                        html.Tr([
                            html.Td('Auto'),
                            html.Td('96.63%')
                        ]),
                        html.Tr([
                            html.Td('Manual'),
                            html.Td('91.77%')
                        ]),
                        html.Tr([
                            html.Td('Overall'),
                            html.Td('96.20%')
                        ])
                    ])
                ], className='kpi-table')
            ], className='kpi-block')
        ], className='kpi-blocks'),
        # MOM Table: Auto/Manual Distribution
        html.Div([
            html.H2('MOM: Auto/Manual Distribution', className='table-title'),
            dash_table.DataTable(
                id='mom-table',
                columns=[{"name": i, "id": i} for i in mom_data.columns],
                data=mom_data.to_dict('records'),
                style_table={'overflowX': 'scroll'},
                style_cell={'textAlign': 'center'},
                merge_duplicate_headers=True
            )
        ], className='mom-table-container')
    ], className='dashboard-block'),

])

CSS:


/* Terminal Text Div */
.terminal-text {
    background-color: #000; /* Black background for terminal-like appearance */
    color: #00ff00; /* Green text color */
    padding: 20px;
    font-family: monospace;
    white-space: pre;
    overflow: auto;
    margin: 20px;
    border-radius: 5px;
}

/* Table Div */
#table-container {
    padding: 20px;
    background-color: #f9f9f9;
    border-radius: 10px;
    box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.1);
    margin: 20px;
}

.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner {
    border-radius: 5px;
    box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.1);
}



@keyframes loading {
  0% { content: "."; }
  33% { content: ".."; }
  66% { content: "..."; }
  100% { content: "."; }
}

.loading:before {
  content: "Loading";
  animation-name: loading;
  animation-duration: 1s;
  animation-timing-function: step-start;
  animation-iteration-count: infinite;
}




.navbar {
    background-color: #f9f9f9;
    color: black;
    padding: 15px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.1);
    font-family: 'Arial', sans-serif; /* Modern font */
}

.dashboard-block {
    padding: 20px;
    background-color: #f9f9f9;
    border-radius: 10px;
    box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.1);
}

.dashboard-title {
    text-align: center;
    color: #333;
    font-weight: 600;
    margin-bottom: 20px;
}

.kpi-blocks {
    display: flex;
    justify-content: space-between;
}

.kpi-block {
    background-color: #fff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.1);
    flex: 1;
    margin: 0 10px;
}

.kpi-block h3 {
    font-size: 1.2rem; /* Bigger header size */
    text-align: center;
    margin-bottom: 15px;
    color: #333;
    font-weight: 600;
}

.kpi-segment {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: 5px 10px; /* Additional margin for differentiation */
    padding: 10px;
    background-color: #f5f5f5; /* Background color for differentiation */
    border-radius: 5px;
}

/* Rest of the CSS code */


.logo {
    font-size: 26px;
    font-weight: 900;
    color: #333; /* Dark gray color for the logo */
}

.search-bar {
    display: flex;
    align-items: center;
    flex-grow: 1;
    justify-content: center;
}

.search-icon {
    background-image: url('search.svg');
    background-size: contain;
    width: 32px;
    height: 32px;
    margin-right: 10px;
}

.search-input {
    width: 300px;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    transition: border 0.3s; /* Smooth transition for the search input */
}

.search-input:focus {
    border-color: #333; /* Darker border when the search input is focused */
}

.header-links {
    display: flex;
    gap: 20px;
    align-items: center;
}

.header-links a {
    color: #333; /* Dark gray color for the links */
    text-decoration: none;
    transition: all 0.3s; /* Smooth transition for hover effects */
    font-weight: 600; /* Semi-bold weight for the links */
}

.header-links a:hover {
    color: #000; /* Black color on hover */
    text-decoration: underline; /* Underline on hover */
}

.user-name {
    margin-right: 10px;
    font-weight: 600; /* Semi-bold weight for the user name */
}

.user-image {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 2px solid #333; /* Border around the user image */
}

.content {
    padding: 20px;
}


.search-box {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 20px;
}

.query-input {
    width: 400px;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    font-size: 16px;
}

.search-button {
    background-color: #007bff;
    color: white;
    border: none;
    padding: 10px 20px;
    margin-left: 10px;
    border-radius: 5px;
    cursor: pointer;
    transition: all 0.3s; /* Smooth transition for hover effects */
}

.search-button:hover {
    background-color: #0056b3; /* Darker color on hover */
}


.kpi-row {
    display: flex;
    justify-content: space-between;
}

.kpi-year {
    text-align: center;
    font-weight: 500;
    color: #555;
}

.kpi-segment {
    display: flex;
    flex-direction: column;
    align-items: center;
}


.kpi-table {
    width: 100%;
    border-collapse: collapse;
    text-align: center;
}

.kpi-table th, .kpi-table td {
    border: 1px solid #ddd;
    padding: 10px;
}

.kpi-table th {
    background-color: #f2f2f2;
}

.kpi-value {
    text-align: center;
    font-weight: 700; /* Bold value */
    color: #333;
}


.table-title {
    text-align: center;
    color: #333;
    font-weight: 600;
    margin: 20px 0;
}

.mom-table-container {
    padding: 20px;
    background-color: #f9f9f9;
    border-radius: 10px;
    box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.1);
}

.mom-table {
    width: 100%;
    border-collapse: collapse;
    text-align: center;
}

.mom-table th, .mom-table td {
    border: 1px solid #ddd;
    padding: 10px;
}

.mom-table th {
    background-color: #f2f2f2;
}

#button-render {
    background-color: #007bff;
    color: white;
    border: none;
    padding: 10px 20px;
    margin-left: 10px;
    border-radius: 5px;
    cursor: pointer;
    transition: all 0.3s; /* Smooth transition for hover effects */
}

#button-render:hover {
    background-color: #0056b3; /* Darker color on hover */
}

Hello @saladgas,

Welcome to the community!

So, in dash, the outputs are expected to be given in the same fashion that you are providing them in the definition:

@app.callback(
    [Output('terminal-text-action', 'children'),
     Output('table-container', 'children')],
    Input('button-render', 'n_clicks'),
    prevent_initial_call=True  # Prevents the callback from being triggered on page load
)
def update_content(n_clicks):
    print(f"Callback triggered with n_clicks: {n_clicks}")  # Debug print
    if n_clicks == 1:
        terminal_text = "It looks like there was a minor reduction in claims being filed manually, further more progress looks to have stagnated in 2023. Want me to dive deep into it?"
        table = create_first_table()
    elif n_clicks >= 2:  # Corrected condition
        terminal_text = "Looks like Lob-1 and Lob 2 received the lowest % of auto claims compared to rest."
        table = create_second_table()
    else:
        raise PreventUpdate

    return terminal_text, table

vs

@app.callback(
    [Output('terminal-text-action', 'children'),
     Output('table-container', 'children')],
    Input('button-render', 'n_clicks'),
    prevent_initial_call=True  # Prevents the callback from being triggered on page load
)
def update_content(n_clicks):
    print(f"Callback triggered with n_clicks: {n_clicks}")  # Debug print
    if n_clicks == 1:
        terminal_text = "It looks like there was a minor reduction in claims being filed manually, further more progress looks to have stagnated in 2023. Want me to dive deep into it?"
        table = create_first_table()
    elif n_clicks >= 2:  # Corrected condition
        terminal_text = "Looks like Lob-1 and Lob 2 received the lowest % of auto claims compared to rest."
        table = create_second_table()
    else:
        raise PreventUpdate

    return [terminal_text, table]

The later should be used since you listed the outputs inside of the list.

Not sure if this is what is causing your issue or not.

Thank you so much for replying, i tried this but still no luck, here is the barebones i am trying with no luck(With no css):

page1.py

from dash import html, dcc
from dash import Output, Input, State
from app import app
from dash.exceptions import PreventUpdate



@app.callback(
    [Output('terminal-text-action', 'children')],
    Input('button-render', 'n_clicks'))

def update_content(n_clicks):
    print(f"Callback triggered with n_clicks: {n_clicks}")  # Debug print
    if n_clicks == 1:
        terminal_text = "It looks like there was a minor reduction in claims being filed manually, further more progress looks to have stagnated in 2023. Want me to dive deep into it?"
    elif n_clicks >= 2:  # Corrected condition
        terminal_text = "Looks like Lob-1 and Lob 2 received the lowest % of auto claims compared to rest."
    else:
        raise PreventUpdate
    return [terminal_text]





layout = html.Div([

    # Search Box and Button
    html.Div([
        html.Button('Search', id='button-render')
    ], className='search-box'),

    # Terminal Text Div
    html.Div(id='terminal-text-action', className='terminal-text'),
])

app.py


from dash import dcc, html, Dash
from dash.dependencies import Input, Output
import page1

app = Dash(__name__)

app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content')
])

@app.callback(
    Output('page-content', 'children'),
    [Input('url', 'pathname')]
)
def display_page(pathname):
    if pathname == '/page1':
        return page1.layout
    else:
        return '404 Page Not Found'

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

Here, try this:

from dash import html, dcc
from dash import Output, Input, State, callback
from app import app
from dash.exceptions import PreventUpdate



layout = html.Div([

    # Search Box and Button
    html.Div([
        html.Button('Search', id='button-render')
    ], className='search-box'),

    # Terminal Text Div
    html.Div(id='terminal-text-action', className='terminal-text'),
])

@callback(
    [Output('terminal-text-action', 'children')],
    Input('button-render', 'n_clicks'))

def update_content(n_clicks):
    print(f"Callback triggered with n_clicks: {n_clicks}")  # Debug print
    if n_clicks == 1:
        terminal_text = "It looks like there was a minor reduction in claims being filed manually, further more progress looks to have stagnated in 2023. Want me to dive deep into it?"
    elif n_clicks >= 2:  # Corrected condition
        terminal_text = "Looks like Lob-1 and Lob 2 received the lowest % of auto claims compared to rest."
    else:
        raise PreventUpdate
    return [terminal_text]
2 Likes

yoooo, yes thank you, what voodo was that

1 Like

howcome app.callback had an issue but callback ran seamlessly?, I love you

1 Like

I think the pages loop through before the app is fully available. So, the pages are out of scope. That’s why you just state the callback.

Also, you should looking into use the pages feature instead of your own index.

1 Like