Black Lives Matter. Please consider donating to Black Girls Code today.

Callback call other callbacks

Hi,

I have two sliders inside a html.Div. when i change the children of the Div, it fire a callback which have a slider as input, why. This is the example code:

import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.config.suppress_callback_exceptions=True

app.layout = html.Div([
                dbc.Button("Add slider",id="button"),
                html.Div([
                    
                ],id='range-sliders'),
                html.Div(id='output-container-range-slider')
            ])

@app.callback(
    Output('range-sliders','children'),
    [Input('button','n_clicks')],
    [State('range-sliders','children')]
)
def addslider(click,children):
    print("2")
    if children:
        if click:
            children[0]['props']['value'] = [0,20]
            return children
        
    else:
        children.append(html.Div([
                dcc.RangeSlider(
                    id='range1',
                    min=0,
                    max=20,
                    step=0.5,
                    value=[5, 15]
                ),
                    dcc.RangeSlider(
                    id='range2',
                    min=0,
                    max=20,
                    step=0.5,
                    value=[5, 15]
                )]))
    return children

@app.callback(
    dash.dependencies.Output('output-container-range-slider', 'children'),
    [dash.dependencies.Input('range1', 'value')])
def update_output(value):
    print("1")
    return 'You have selected "{}"'.format(value)


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

Note: when I click in button, the ouput show:
2
1
2
1
2
1

nobody? I would like modify the children without call the other callback

Hey @JoseMarqueses,

You can use “callback context” in order to fire the callback when a specific property is triggered. It uses the global variable dash.callback_context which is available in callbacks to access the list of changed properties within a callback. You can learn more about it in the Dash documentation FAQs here.

I’ve modified your code below to use callback context to only fire the children after you press the button, I think that’s what your post above is looking for. Let me know if that’s on point. Cheers.


import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import pdb

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.config.suppress_callback_exceptions = True

app.layout = html.Div([
    html.Div(id='test'),
    dbc.Button("Add slider", id="button"),
    html.Div([

    ], id='range-sliders'),
    html.Div(id='output-container-range-slider')
])


@app.callback(
    Output('range-sliders', 'children'),
    [Input('button', 'n_clicks')],
    [State('range-sliders', 'children')]
)
def addslider(click, children):
    print("2")
    if children:
        if click:
            children[0]['props']['value'] = [0, 20]
            return children

    else:
        children.append(html.Div([
            dcc.RangeSlider(
                id='range1',
                min=0,
                max=20,
                step=0.5,
                value=[5, 15]
            ),
            dcc.RangeSlider(
                id='range2',
                min=0,
                max=20,
                step=0.5,
                value=[5, 15]
            )]))
    return children


@app.callback(
    Output("output-container-range-slider", "children"),
    [Input("button", "n_clicks"),
     Input("range-sliders", "children")],

)
def update_output(clicks, value):
    print("1")
    ctx = dash.callback_context
    if "range-sliders.children" in ctx.triggered[0]['prop_id']:
        return 'You have selected "{}"'.format(value[0]['props']['children'][0]['props']['value'])


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

and this case?

import dash
from dash.dependencies import Input, Output, State
import dash_html_components as html
import dash_core_components as dcc
import ast


external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.config.suppress_callback_exceptions=True

app.layout = html.Div([
    html.Button('Add a slider', id='button', n_clicks=0),
    html.Div(children=[],id='slider-container'),
    # this is a hack: include a hidden dcc component so that
    # dash registers and serve's this component's JS and CSS
    # libraries
    dcc.Input(style={'display': 'none'})
])

@app.callback(
    Output('slider-container', 'children'),
    [Input('button', 'n_clicks')],
    [State('slider-container','children')])
def add_sliders(n_clicks,children):
    print("1")
    #global children
    if children:
        if n_clicks:
            print(children)
        return children
    else:
        for i in range(10):
            children.append(html.Div(
                [html.Div([
                html.Div(dcc.RangeSlider(
                    id='slider-{}'.format(i),
                    min=0,
                    max=20,
                    step=0.5,
                    value=[5, 15]
                ),),
                html.Label(id='output-{}'.format(i), style={'marginTop': 30})
            ])]
            ))
        return children

def generate_input():
    c = [Input('slider-{}'.format(i), 'value') for i in range(10)]
    return tuple(c)

def generate_output():
    c = [Output('output-{}'.format(i), 'children') for i in range(10)]
    return tuple(c)

@app.callback(
    [a for a  in generate_output()],
    [a for a  in generate_input()])
def update_output(*slider_i_value):
    print("2")
    ctx = dash.callback_context
    if ctx.triggered:
        print(ctx.triggered)
        print("3")
        return slider_i_value

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

When you start app, Update_output is called and if you change a value of a range-slider Update_output save the last call and is called twice. This is an example but i need this for a proyect. And it’s possible return only a value when you have more than one outputs? and return value to specific Output.

Thank!