Chained callbacks don't get called

Hello!

I am leaning how chained callbacks work in dash and stalled a bit, can someone more experienced have a look at the code below and show where I am doing it wrong?

The main idea is to use output callback that uses long_lasting_query() as an input to another callbacks, but it looks like no callbacks were called at all. I am 100% sure that I am missed something very simple, but couldn’t spot the issue, so please help.

Self sufficent code below, I am using the last available release if Dash & Plotly.

import argparse

import dash_bootstrap_components as dbc
import pandas as pd
from dash import Dash, Input, Output, State, dcc, html
from dash.dash_table import DataTable


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Dash example",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )

    parser.add_argument(
        "-a",
        "--addr",
        type=str,
        metavar="arg",
        required=True,
        help="address to bind to (ip:port)",
    )
    parser.add_argument("--debug", action="store_true", help="run in debug mode")

    args = parser.parse_args()

    return args


def long_lasting_query() -> pd.DataFrame:
    df = pd.read_csv(
        "https://raw.githubusercontent.com/plotly/datasets/master/gapminder_unfiltered.csv"
    )

    return df


columns = ["country", "continent", "year", "lifeExp", "pop", "gdpPercap"]

app = Dash(
    external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True
)

app.layout = dbc.Container(
    [
        html.P(
            "Year: ",
            style={"display": "inline-block", "margin-right": "8px"},
        ),
        dcc.Input(id="year", type="number", min=0),
        html.Button("Submit", id="submit-val", n_clicks=0),
        DataTable(
            columns=[{"name": c, "id": c} for c in columns],
            id="tbl",
            page_size=20,
            sort_action="native",
            style_header={
                "textAlign": "center",
            },
        ),
    ]
)


@app.callback(
    Output("db_data", "value"),
    Input("submit-val", "n_clicks"),
    State("year", "value"),
    prevent_initial_call=True,
)
def load_data(n_clicks, value):
    df = long_lasting_query()
    df = df[df["year"] == value]
    if df.empty:
        return "{}"

    return df.to_json(orient="split")


@app.callback(
    Output("tbl", "data"),
    Input("db_data", "value"),
)
def update_table(value):
    df = pd.read_json(value, orient="split")
    if df.empty:
        return "{}"

    return df.to_dict("records")


def main() -> None:
    args = parse_args()
    host, port = args.addr.split(":")
    app.run(host=host, port=port, debug=args.debug)


if __name__ == "__main__":
    main()

Hi @ksqrtr, you’re not too far indeed. You can check how to share data between callbacks here.

Basically, in your first callback you want to store your db_data inside a dcc.Store component that you’ll be able to pass as an input to your second callback. Note that dcc.Store only stores data as json (or dict).

dcc.Store(id=`db_data`)
...
@app.callback(
    Output("db_data", "data"),
    Input("submit-val", "n_clicks"),
    State("year", "value"),
    prevent_initial_call=True,
)
def load_data(n_clicks, value):
   ...

@app.callback(
    Output("tbl", "data"),
    Input("db_data", "data"),
)
def update_table(data):
    ...

Let me know how this goes

Thanks a lot! Helped.