I have a single page Dash app that includes a dash_table
of users, user-select-table
. When you click on a cell in that table, the user represented by that row is “selected” and other components on the page update to show information about that user. I want to make it easy to jump to the selection of a specific user, so that if you go to myapp[.]com/?user_id=3, the app loads with User ID 3 already selected. Furthermore, when you click on a cell in user-select-table
, the app URL updates to include ?user_id=<selected user ID>
. The idea is that someone looking at user ID 5 can easily copy/paste the URL from their URL bar and share it with someone else, and when that someone else follows the link, the app will open with user ID 5 selected and all of the components on the page are displaying that user’s info.
Essentially I want to use the value of a URL query parameter when rendering the initial layout, but after that I want the URL query parameter to be exclusively an Output
of my callback, and never an Input
.
The docs here show how to do this if using dash pages, but I want to keep my app single-page.
The code for an example app, modeled after the example in the docs, is below. When I go to the app’s URL without any URL query parameters (e.g. localhost:8050/), the user-id-message
reads “You have selected user: 1”, as expected. When I click on a cell in the table (let’s say User ID 3), the URL updates as expected, to localhost:8050/?user_id=3. But when I open a new browser window and navigate to localhost:8050/?user_id=3, User ID 1 is selected. How can I get my single-page app to use the URL query parameter as it’s rendering its initial layout?
import pandas as pd
from dash import Dash, dcc, dash_table, Input, Output, callback, html, no_update
# Example data
df = pd.DataFrame({
'UserID': [1, 2, 3, 4, 5, 6],
'SessionID': [1001, 1002, 1003, 1004, 1005, 1006],
'Group': ["A", "A", "A", "B", "B", "B"],
'Value': [25.94, 73.79, 22.76, 32.69, 15.82, 8.11]
})
df["id"] = df["UserID"] # for dash_table's row_id
app = Dash(__name__)
def layout(**kwargs):
user_id = kwargs.get("user_id")
if user_id is None or user_id not in df["id"].values:
user_id = df["id"].values[0]
selected_user_row_ix = df.index[df['UserID'] == user_id].tolist()[0]
user_table = dash_table.DataTable(
id="user-select-table",
columns=[
{"name": "User ID", "id": "UserID"},
{"name": "Session ID", "id": "SessionID"},
{"name": "Group", "id": "Group"},
{"name": "Value", "id": "Value"},
],
fixed_columns={"headers": True, "data": 0},
data=df.to_dict("records"),
active_cell = {
"row": selected_user_row_ix,
"column": 0
},
is_focused = True
)
return(html.Div([
dcc.Location(id="url", refresh=False),
html.H1('Hello Dash'),
html.Div(f'You have selected user: {user_id}.', id="user-id-message"),
user_table
]))
app.layout = layout
@callback(
Output("url", "search"),
Output("user-id-message", "children"),
Input("user-select-table", "active_cell"),
prevent_initial_call=True
)
def update_selected_user(active_cell):
if not active_cell:
return(no_update, "Select a user")
selected_user_id = active_cell.get("row_id")
new_msg = f'You have selected user: {selected_user_id}.'
return(f"?user_id={selected_user_id}", new_msg)
if __name__ == '__main__':
app.run()