Sorry, in the end since get_closest_address
was called every time I ended up doing something even simpler without an additional callback.
@callback(
[Output("locations", "options"),
Output("locations", "value"),
Output("locations-list", "data")],
Input("search-button", "n_clicks"),
[State("from_address", "value"),
State("locations-list", "data")]
)
def get_closest_address(n_clicks, from_address, locations):
if n_clicks is None:
# In this case it means that the button has not been clicked
# so we first check if there are already some locations
# saved in the cache
# If there is no data in the cache, locations will be an empty dict
if len(locations) > 0:
locations = pd.read_json(
locations, orient='split', dtype={"id": str})
else:
# In this case it means the button has not been clicked AND
# there is no data in the Store component
raise PreventUpdate
else:
# In this case the button has been clicked so we load the data
locations = get_locations(from_address)
options = []
for _, row in locations.iterrows():
options.append(
{
"label": (
f"{row['name']} ({row['country']} | {row['longitude']:.1f}E, "
f"{row['latitude']:.1f}N, {row['elevation']:.0f}m)"
),
"value": str(row['id'])
}
)
if n_clicks is None:
# In this case we need to update everything besides the value
# because it is persisted, so if the user already selected something
# it will be there
return options, no_update, locations.to_json(orient='split')
else:
# If we're here, it means the button has been clicked, so we need
# to update everything, and set the value as the first option (default)
return options, options[0]['value'], locations.to_json(orient='split')
Note that I removed the prevent_initial_call
because this should be evaluated every time the page refreshes or the button is pressed.
Every time I get into the callback I check if the button has actually been pressed. If yes, I then proceed to check if there is already data in the Store
component, which is locally persisted. If yes, then I use that data to populate the options of the Select
component. I do not update the value
attribute in this case because this is also persisted, so I get whatever was already selected before.
If there is no data in Store
and the button has not been pressed I don’t do anything.
Otherwise, if the button has been pressed I compute again the locations and populate both options
and value
attributes of the Select
.
I don’t know how clean this is, but it seems to work pretty well.
There is still something weird:
- I still don’t understand why this callback is called every time the page is refreshed if the
Input
is only n_clicks
, but I guess it’s because the layout is served every time…
- If the user does not select the option in the
Select
then the value
property is not persisted, even though it is indeed set. maybe it is because setting via callback does not allow it to be persisted? I don’t know