Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dcc location callback nav #2068

Merged
merged 8 commits into from
Feb 27, 2023
Merged

Conversation

AnnMarieW
Copy link
Collaborator

@AnnMarieW AnnMarieW commented May 27, 2022

Improve page navigation when dcc.Location is updated in a callback.

Currently when dcc.Location is updated in a callback, the browser url is updated, but it does not navigate to the new page unless the browser is refreshed. This PR makes it unnecessary to refresh the browser -- giving the same fast navigation as when user clicks on a dcc.Link.

As requested on the Dash Community Forum

Limitations

The following issues occur when there are multiple dcc.Location components in a multi-page app. These are all existing issues and are not new based on changes in this PR. It they aren't fixed as part of the PR, then it would be helpful to document the workarounds.

  • There are potential conflicts when callbacks update multiple dcc.Location location causing the browser to crash.

  • When using in a multi-page app with multiple dcc.Location components, they must all be on the same page - typically the app.py file. (Or the index.py if using the multi-page structure where app is defined in it's own file)

  • When navigating to a page with the callback to update the dcc.Location the callback is fired even when prevent_initial_call=True This has been reported in issue 1346 and issue 1985

  • Browser forward/back doesn't work when there are multiple dcc.Location components.

    • For Pages apps, this is working now 🎉 This was solved by adding refresh="callback-nav" to the dcc.Location in the dash.page_container

Contributor Checklist

  • I have run the tests locally and they passed. (refer to testing section in contributing)
  • I have added tests, or extended existing tests, to cover any new features or bugs fixed in this PR

optionals

  • I have added entry in the CHANGELOG.md

Sample usage

import dash
from dash import Dash, html, dcc, Input, Output, State

app = Dash(__name__, use_pages=True, pages_folder="")


def layout_stocks(stock=None):
    return html.Div(f"Stock analysis for {stock}")


dash.register_page("stocks", layout=layout_stocks, path_template="/stocks/<stock>")
dash.register_page("home", path="/", layout=html.Div("home page"))

app.layout = html.Div(
    [
        dcc.Location(id="url", refresh="callback-nav"),
        html.Div("Enter Stock Symbol"),
        dcc.Input(id="stock-input"),
        html.Button("submit", id="submit", n_clicks=0),
        html.Hr(),
        dash.page_container,
    ]
)


@app.callback(
    Output("url", "href"), Input("submit", "n_clicks"), State("stock-input", "value")
)
def go(n, stock):
    if n > 0:
        return f"/stocks/{stock}"
    return dash.no_update


if __name__ == "__main__":
    app.run(debug=True)

@AnnMarieW AnnMarieW marked this pull request as ready for review December 13, 2022 20:57
@T4rk1n
Copy link
Contributor

T4rk1n commented Feb 7, 2023

The PR look good.

Is there an issue for the limitation:

There are potential conflicts when callbacks update multiple dcc.Location location causing the browser to crash.

@alexcjohnson
Copy link
Collaborator

This looks good to me too. As @T4rk1n says we should make sure there's an issue open regarding the browser crash scenario, and any of the other limitations we aren't addressing here. In particular:

When using in a multi-page app with multiple dcc.Location components, they must all be on the same page

Does that include the automatic one added by the pages skeleton? If so is the statement actually "none of the individual pages can have a dcc.Location, only the main app.layout? Why would you want to do this anyway, and does this apply even if those other dcc.Location components are only used as inputs?

Browser forward/back doesn't work when there are multiple dcc.Location components

Only with refresh=True now, I gather, since as you say it's fixed for 'callback-nav'. Is there a concise statement we can make in the docs about when to use each of the three refresh options? Something like:

  • Use True to navigate outside this Dash app
  • Use False if the same callback that updates the Location component is also updating the page content
  • Use 'callback-nav' if a different callback will respond to the new Location with updated content.

Do I have that right or is there more nuance (or things I got wrong)?

Anyway none of this needs to go in this PR, but before merging we should have answers/issues for these points. And resolve the merge conflicts 😏

@AnnMarieW
Copy link
Collaborator Author

AnnMarieW commented Feb 25, 2023

Merge conflicts are fixed

Here is clarification on the use-case and limitations:

  • Use True to navigate outside the Dash app or to manually refresh a page.
  • Use False if the same callback that updates the Location component is also updating the page content - typically used in multi-page apps that do not use Pages.
  • Use 'callback-nav' if a different callback will respond to the new Location with updated content. This is typical with multi-page apps that use Pages. This will allow for navigating to a new page without refreshing the page

Limitations

There are potential conflicts when callbacks update multiple dcc.Location location causing the browser to crash.

This was reported in #1346. I added a MRE there.

To make components available for all pages of a multi-page app the component must be in the app.py file.

Navigation can be buggy if the dcc.Location is in one of the pages rather than in app.py. Added a MRE to issue #1346

Browser forward/back doesn't work when there are multiple dcc.Location components

I added a minimal example in #1312

Copy link
Collaborator

@alexcjohnson alexcjohnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💃 Thanks @AnnMarieW for adding this mode, and for adding examples for the remaining buggy behavior 🎉

@LiamConnors would you look over the description @AnnMarieW posted for when to use each of these three refresh modes #2068 (comment) and ensure they're reflected in the docs?

@alexcjohnson alexcjohnson merged commit 72b8a91 into plotly:dev Feb 27, 2023
@AnnMarieW
Copy link
Collaborator Author

AnnMarieW commented Feb 27, 2023

@alexcjohnson once @LiamConnors reviews the descriptions, should I update the docstrings for the referseh prop as well?

@alexcjohnson
Copy link
Collaborator

Sure, that could be useful 🌟
Probably doesn't need to be coordinated - you can update the docstring now if you like!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants