How to disable a dcc.Link given that html a can't be disabled?

Sounds like a dumb question but here is the situation:

I have a top navigation bar, in which there are several dcc.Link.
When I click on each of these dcc.Link (which have the “a” tag in my html code), the url pathname is updated with the target page name, and the content of my page is updated. Without refreshing the page.

Now, let’s say a user finetunes the graph, displays what he wants, and mistakely click again on the dcc.Link. The consequence is that it triggers the callback again, and all the “parameters” chosen by this user are reset.

So, I’m trying to disable this dcc.Link/a tag after a user clicked on it a first time.

Ideally, this basic callback:

    @app.callback(Output('page-content', 'children'),
                  [dash.dependencies.Input('url', 'pathname')])
    def display_page(pathname):
        if pathname == '/homepage':
            return homepage
        elif ....
            return

would have to check first that the current page, the one displayed, is different from the one we ask.

An alternative would be to have a button instead of a dcc.Link, and triggers some Javascript to refresh the url with a window.location.pathname=…

Does anyone here already find a way to do this?

First thing that comes to mid is wrapping a html.Div around the dcc.Link, and set the Div's style property to {'display':'none'} to hide and {'display':'block'} to show, based on your needs.

1 Like

Thanks @flyingcujo, but the problem with this solution was that if you hide the third Div, then Divs 1 and 2 move to the right.

So, I’ve fixed the issue this way:
First, I wrapped every menu item in a Div, as you suggested,
Second, I’ve defined 2x8 variables, one for each element of the navbar menu, like this:
elem_A_L=dcc.Link(…)
elem_B_L=dcc.Link(…)

elem_H_L=dcc.Link(…)

elem_A_D=html.Div(…)
elem_B_D=html.Div(…)

elem_H_D=html.Div(…)

Third, my callback whose input is dcc.Location now updates 9 elements each time:
1 element is the page-content,
and 8 elements are the menu items, so that if one clicks on “item A”, then the callback returns
page-content, elem_A_D, elem_B_L, elem_C_L etc…

It’s ugly but it works.

Would be great if it was possible to change the type of an element with Dash directly, from “a” to “Div” for instance. Would this make any sense to you, @chriddyp ?

Dash-bootstrap-components might provide some relief - depending on the nature of your app. See https://dash-bootstrap-components.opensource.faculty.ai/

I tried bootstrap a couple of weeks ago, but I didnt like it. I opted for tailwind.css instead. More flexible.

In the meantime, I found a better way using State, and the raise PreventUpdate function.

Thanks to PreventUpdate(), I can prevent the callback execution. Which means that the page-content is not refreshed. Neither are the style of the link, if they have been clicked once already.

the callback takes 1 input (url, pathname), and as many State (linkID, style) as there are menus in my navbar.

Then the logic is:

def display_page(pathname, elem_1_style, elem_2_style,....):
    if pathname == '/homepage':
        if elem_1_style== None:
            return homepage, styleWhenButtonIsOn, styleWhenButtonIsOff
        else:
            if str(elem_1_s)==str(styleWhenButtonIsOff):
                return homepage, styleWhenButtonIsOn, styleWhenButtonIsOff
            else: 
                raise PreventUpdate()

    elif pathname == '/page2':
        if elem_2_s== None:
            return page2, styleWhenButtonIsOff, styleWhenButtonIsOn
        else:
            if str(elem_2_s)==str(styleWhenButtonIsOff):
                return page2, styleWhenButtonIsOff, styleWhenButtonIsOn
            else: 
                raise PreventUpdate()

None is required because the first time the menu is generated, and loaded, there is still no Style applied in the “style” attribute of me element because I use tailwind, and every style is applied using className. I could have used className, in my States, then, but it would have been longer. Here, I just apply a background-color, and a color in the style attribute, and it’s done :slight_smile:

It works like a charm. :smiley:

Note: the styleWhenButtonIsOn is:
cBtn={‘background-color’:’#fff’,
‘color’:’#333’}
and styleWhenButtonIsOff:
uBtn={‘background-color’:’#333’,
‘color’:’#fff’}

Note2: a click on the element whose style is elem_1_style displays the homepage, and a click on the element whose style is elem_2_style displays the page2

1 Like