Search code examples
pythonleafletplotly-dashdash-leaflet

How to update an input component from a callback in Dash app


Context :

I have a Dash app with a dropdown and a map.

The map displays 3 markers with IDs : '01', '02' and '03'. I am able to click on a marker and return its ID through a callback.

The dropdown enables me to choose a marker by its ID.

What I want to do is to update the selected value in the dropdown with the selected marker on the map.

Example : I want to choose '02'. I can do it either by selecting '02' in the dropdown, or clicking on '02' on the map. But if I select it from the map, I want the selected value from the dropdown to be updated to '02'.

How can I update the Dropdown selected value from a value returned by a callback ?

Here is a reprex :

# -*- coding: utf-8 -*-
import dash.dependencies
import dash_bootstrap_components as dbc
import pandas as pd
import dash_leaflet as dl
from dash import dcc, html, Input, Output

########################################################################################################################
# VARIABLES
df_sites = pd.DataFrame([['01', (48.805, 2.326)], ['02', (43.167, 0.969)], ['03', (48.812, 2.530)]],
                        columns=['id_tech', 'coordonnees'])

circlemarkers = [dl.CircleMarker(
    dl.Tooltip(df_sites.iloc[i]['id_tech']),
    center=pos,
    radius=2,
    id=df_sites.iloc[i]['id_tech'])
    for i, pos in enumerate(df_sites['coordonnees'])]

########################################################################################################################
# APP
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = html.Div([
        dbc.Col([
            dbc.Row([
                # Following dropdown selected value should be updated when clicking on the map
                dcc.Dropdown(df_sites['id_tech'].tolist(), df_sites['id_tech'].tolist()[0],id="in_choix_site"),
                html.Br(),
                dl.Map([dl.TileLayer(),
                        dl.GestureHandling(),
                        dl.LayerGroup(id="container", children=circlemarkers)
                        ],
                       center=[47.285549, 2.300576],
                       zoom=4,
                       style={'height': '200px', 'width':'90%', 'margin':'10px'}),

                "Selected from dropdown",
                html.Div(id='from_dropdown'), 
                html.Br(),
                "Selected from map",
                html.Div(id='from_map')
            ])
        ], md=4)
])

@app.callback(Output("from_dropdown", "children"),
              Input("in_choix_site", "value"))
def selected_site(dropdown_value):
    return(dropdown_value)

@app.callback(Output("from_map", "children"),
              [Input(marker.id, "n_clicks") for marker in circlemarkers])
def marker_click(*args):
    marker_id = dash.callback_context.triggered[0]["prop_id"].split(".")[0]
    return marker_id

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

Solution

  • So thanks to a coworker, I got the answer !

    It was actually pretty obvious but since I am new to Dash I didn't figured it out right away. I just needed to add my Dropdown as an output of a callback. I am leaving this answer here in case someone else is having the same issue.

    @app.callback([Output("from_map", "children"),
                   Output("in_choix_site", "value")],
                  [Input(marker.id, "n_clicks") for marker in circlemarkers])
    def update_dropdown(*args):
        marker_id = dash.callback_context.triggered[0]["prop_id"].split(".")[0]
        return marker_id, marker_id