I am facing an issue using Altair in a Dash project.
I am building an app using Dash. The plot to display on it is generated using Altair instead of Plotly-Dash. When I run the app I am receiving an error with the following message:
dash.exceptions.InvalidCallbackReturnValue: The callback for `[<Output `my-graph.figure`>, <Output `download2.data`>]`
returned a value having type `tuple`
which is not JSON serializable.
(This error originated from the built-in JavaScript code that runs Dash apps. Click to see the full stack trace or open your browser's console.)
TypeError: Cannot read properties of null (reading 'layout')
In general, Dash properties can only be dash components, strings, dictionaries, numbers, None, or lists of those.
By the last part, I think this issue is being generated by Altair plot.
So my question is: how can I solve this? I prefer to use Altair instead of Plotly.
Do any of you face a similar issue?
You can see detailed examples for how to combine Altair with different dashboarding frameworks in my answer here. For Dash, there is no official support for and Altair plot class, but you can use an iframe to render your plot:
import altair as alt
from vega_datasets import data
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
cars = data.cars()
# Setup app and layout/frontend
app = dash.Dash(__name__, external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css'])
app.layout = html.Div([
dcc.Dropdown(
id='x_column-widget',
value='Miles_per_Gallon', # REQUIRED to show the plot on the first page load
options=[{'label': col, 'value': col} for col in cars.columns]),
html.Iframe(
id='scatter',
style={'border-width': '0', 'width': '100%', 'height': '400px'})])
# Set up callbacks/backend
@app.callback(
Output('scatter', 'srcDoc'),
Input('x_column-widget', 'value'))
def plot_altair(x_column):
chart = alt.Chart(cars).mark_point().encode(
x=x_column,
y='Displacement',
tooltip='Horsepower').interactive()
return chart.to_html()
if __name__ == '__main__':
app.run_server(debug=True)