Loop through Dash pages with callbacks or javascript

I’m trying to figure out a method for looping through pages on my dashboard over an interval, can anyone provide any guidance? I implemented the interval with dcc.Interval, and that works. I have a multi page app that contains a few pages, using Dash’s multi page API. When the cycle button is turned on, I want to be able to cycle through the pages/navlinks that were created in the layout like this:

        dbc.Nav(
            [
                dbc.NavItem(dbc.NavLink(page["name"], href=page["path"], active="exact", id=page["name"] + "-navlink")) for page in dash.page_registry.values()
            ],
            id="sidebar-nav",
            vertical=True,
            pills=True,
        ),

I’ve tried callbacks that edit the href of a dcc.Location or dcc.Link but none of them update the page when the href is changed. I need the callback to just change the page content to the right of the sidebar without refreshing the whole page (like when clicking on a navlink on the left). Is there a way to click the navlinks programmatically?

Hello @gavb,

Welcome to the community!

You have tried to loop through and click, is there a reason why you are trying to do this exactly? It seems a little strange to navigate pages automatically.

However, I’m wondering if something like this might be what you are after:
https://dash-bootstrap-components.opensource.faculty.ai/docs/components/carousel/

Hi @jinnyzor,

Thanks!

There are times where this dashboard will be displayed on a TV screen, and I want it to be able to automatically switch between the dash pages on the sidebar on the left (when cycle toggle is turned on, start at home, then every N seconds switch to the next page, and then the next, and then loop back to the first and so on). Is there a way to programmatically click the navlinks every N seconds over the dcc.Interval, or to programmatically follow the link for dcc.Link when the href is changed, instead of having to assign it to a button and manually click it?

Basically, I would like a method that would programmatically loop through the menu items/pages while the cycle toggle is turned on, and then stop looping through them when turned off. I have the logic for the cycle toggle, but can’t get the pages to redirect properly. Sorry if this sounds a bit confusing, please let me know if I need to explain in more detail!

It’s similar to Loop through different apps after a specified period of time, but I’m using the newer built in Dash Pages feature and didn’t want to manually assign layouts.

Oh, ok, yeah, that makes sense.

You could use a clientside callback to click on the links, then it acts exactly like a user. :slight_smile:

The easiest way would probably be a jquery function:

app.clientside_callback(
"""
function (n) {
    if (n) {
        $($("#navbar a")[n-1]).click()
        if (n == $("#navbar a").length) {
            return 0
        }
    }
    return window.dash_clientside.no_update
}
""",
Output('intervals','n_intervals'),
Input('inverals','n_intervals')
)

So, something like this will iterate through and then reset your n_intervals when it reaches the end of the list. This is where navbar is the id of your navigation bar.

You’ll need to import the jquery library as an external_scripts in order for this to work.

Thanks so much for your help @jinnyzor !

Looks like I got it to work (with one small change on the click line)

app.clientside_callback(
"""
function (n) {
    if (n) {
        $("#sidebar-nav a")[n-1].click()
        if (n == $("#sidebar-nav a").length) {
            return 0
        }
    }
    return window.dash_clientside.no_update
}
""",
Output('interval', 'n_intervals'),
Input('interval','n_intervals')
)

I do need one small change however; when I click cycle, some of the navlinks are hidden using a callback I made by setting{"display": "none"}. This had to happen because some of these pages cannot be shown on the TV display. Is there a way to only cycle the navlinks whose display is not none using this same method?

Sure:

app.clientside_callback(
"""
function (n) {
    if (n) {
        $("#sidebar-nav a:visible")[n-1].click()
        if (n == $("#sidebar-nav a:visible").length) {
            return 0
        }
    }
    return window.dash_clientside.no_update
}
""",
Output('interval', 'n_intervals'),
Input('interval','n_intervals')
)

Just add a visible query. :slight_smile:

@jinnyzor this solves my issue perfectly, thanks so much!

I thought I had read somewhere that JQuery doesn’t play nice with Dash, so I had mostly abandoned the idea of using it. FAQs | Dash for Python Documentation | Plotly says that JQuery mostly can’t be used with Dash (although this works perfectly, so not sure why this is stated). Is this an older FAQ, or are they referencing other JQuery functions that can’t be used with Dash?

In some instances, JQuery doesnt work well. (Updating values via JS causes sync issues between the React props / server) You can usually solve this with other clientside callbacks.

Things like chart clicks are also not really available with JQuery, most often you will have to use dispatchEvent() from vanilla.

However, things with n_click properties work for JQuery. :wink:

1 Like