Plotly.io.to_json() in clientside callback

hello I am trying to output a JSON figure object coming from my backend api to be passed into dcc.Graoh() but it is causing some probelms.
I am using a clientside_callback function to invoke the api, get the JSON data of the figure and then return it as output for my dcc.Graph(). Below is my code:

clientside_callback(
    """
        async function(rel, indep, dep, viewname, input_value) {
            query_strings = JSON.parse(input_value)
            const request_obj = // request object for api 
            if (viewname && rel) {
                // hit an api to get the figure in JSON format...
                const data = await response.json();
                console.log("graph obj",JSON.parse(data["data"]));
                return JSON.parse(data["data"]); // data contains the JSON from plotly.io.to_json()
            }
        }
    """,
    Output("graph-component", "figure"),
    # Input("view-input", "value"),
    Input("relation-dropdown", "value"),
    Input({"type": "indep-filter-dropdown", "index": ALL}, "value"),
    Input({"type": "dep-filter-dropdown", "index": ALL}, "value"),
    State("view-name", "value"),
    State("input", "value")
)```

Hello @ghulam_shabbir_khan,

You should be returning the complete figure, right now you are only returning the data, or so it seems.

The data actually contains the Figure object in JSON format

What happens if you return a window.dash_clientside.no_update outside of the if statement?

Do you see any console errors?


This is the error on my console when the callback fires keep in mind that the console.log(“graph obj”) line does return the json data and the error is being raised after that line of code.

Could you please provide an MRE?

This is a little difficult to diagnose.

1 Like

I’m sorry but What is an MRE?

A minimum-reproducible-example:

It helps with troubleshooting

Oh thanks for sharing the resource. Here it is:

import dash
from dash import Dash, dcc, html, Input, Output, ALL, Patch, callback, dash_table, clientside_callback, State
import requests
import json

# app = Dash(__name__)

dash.register_page(__name__, path='/multicallback-component')


# Page Layout for Different Filters and Dropdown Selections
def layout(**query_strings):
    print("query_strings", query_strings)
    qualitative_vars = None
    quantitative_vars = None
    relation = ["Count", "Sum", "Average", "Mean and Standard Error", 'percentage', "No Relation"]
    if query_strings.get('prodclidsid') and query_strings.get('tid'):
        response = // Hit an api to get metadata for dropdowns
        if response.status_code == 200:
            data = response.json()
            qualitative_vars = data
            quantitative_vars = data

            if data:
                layout = html.Div(
                    [
                        dcc.Input(value=f"{json.dumps(query_strings)}", id="input",
                                  type="text",
                                  placeholder="",
                                  style={'marginRight': '10px', 'display': 'none'}),
                        dcc.Input(value=query_strings.get("viewname"), id="view-name",
                                  type="text",
                                  placeholder="",
                                  style={'marginRight': '10px', 'display': 'none'}),
                        dcc.Input(value=quantitative_vars, id="quantitative_vars", type="text", placeholder="",
                                  style={'marginRight': '10px', 'display': 'none'}),
                        dcc.Input(value=qualitative_vars, id="qualitative_vars", type="text", placeholder="",
                                  style={'marginRight': '10px', 'display': 'none'}),
                        html.Button("Add Trace", id="add-trace-btn", n_clicks=0),
                        html.Div(id="dropdown-container-div", children=[]),
                        dcc.Dropdown(id="relation-dropdown", options=relation),
                        dcc.Graph(id="graph-component", figure={}),
                    ]
                )

                return layout


clientside_callback(
    """
        async function(rel, indep, dep, viewname, input_value) {
            query_strings = JSON.parse(input_value)
            const request_obj = // request object for api 
            if (viewname && rel) {
                // hit an api to get the figure in JSON format...
                const data = await response.json();
                console.log("graph obj",JSON.parse(data["data"]));
                return JSON.parse(data["data"]); // data contains the JSON from plotly.io.to_json()
            }
        }
    """,
    Output("graph-component", "figure"),
    # Input("view-input", "value"),
    Input("relation-dropdown", "value"),
    Input({"type": "indep-filter-dropdown", "index": ALL}, "value"),
    Input({"type": "dep-filter-dropdown", "index": ALL}, "value"),
    State("view-name", "value"),
    State("input", "value")
)


@callback(
    Output("dropdown-container-div", "children"),
    Input("add-trace-btn", "n_clicks"),
    State("qualitative_vars", "value"),
    State("quantitative_vars", "value"),
)
def display_dropdowns(n_clicks, qualitative_vars, quantitative_vars):
    patched_children = Patch()
    new_dropdown_1 = dcc.Dropdown(
        options=qualitative_vars,
        id={"type": "indep-filter-dropdown", "index": n_clicks},
    )
    new_dropdown_2 = dcc.Dropdown(
        options=quantitative_vars,
        id={"type": "dep-filter-dropdown", "index": n_clicks},
    )
    patched_children.append(new_dropdown_1)
    patched_children.append(new_dropdown_2)
    return patched_children

@jinnyzor I have looked into the JSON and found that the json of the figure was in the value of another key named data (which I was trying to render on the dcc.Graph() component).

{
    "data" : {
               "data" : [], 
               "layout" : [] 
             } 
}
1 Like

thank you for share with us.

1 Like

Glad you got it figured out.

Your MRE still wasn’t quite enough to figure it out, considering you stripped out the part where you were sending the structure back.

The figure object of the dcc.Graph is supposed to be data, layout and optionally frames. :grin:

1 Like

Yeah sorry, I had to do it due to some professional reasons. I’m glad for the help you provided on every reply, Thanks :innocent:

1 Like

For future reference, you can always make api calls using the underlying Flask paths:

@app.server.route('/fill_graph', methods=['POST'])
def fill_graph():
    ## populate your data here and return (flask.request.json provides the body)

Thank you for the suggestion!

1 Like