Dash on Multi-Page Site, @app.route? (Flask to Dash)


#1

I am somewhat familiar with building multi-page sites using Flask, and I am now exploring the use of Dash because I need an easy to implement methodology for getting interactive data on some pages of a website.
I read in yesterday’s announcement that “Dash applications are web servers running Flask and communicating JSON packets over HTTP requests.”

So, Flask apps use @app.route(’/’), @app.route(’/datapage’), et cetera to direct towards different pages…

I am hoping that there is a way to do this with Dash. However, the only examples I have found so far are made for single page Dash apps, and they build the layout directly into the main app (app.layout) and they use @app.callback

Is there a way to use @app.route(’/target’) to specify a page and then to define the app.layout subordinate to whatever route is called?
And, if so, what is the appropriate way to do an @app.callback from within an @app.route(‘x’)?

Thanks!
Chad


Multiple Dashboards?
Autocomplete with Dropdown object when setting app.layout dynamically
URL Not Found - Multi-Page Dash App
#2

Edit - This behaviour has changed. See Dash on Multi-Page Site, @app.route? (Flask to Dash) for an updated answer or https://plot.ly/dash/urls for a tutorial on multi-page apps.


(This answer is now outdated.)

Good question @repower! URLs are possible right now but they are very beta and so I have not documented them. The way we do URLs is going to change in the next few weeks.

You’re welcome to use the beta functionality for now if you’d like. The Dash User Guide uses the beta functionality to render the multi-page user guide, and so you can dig through the source here: https://github.com/plotly/dash-docs/blob/master/tutorial/run.py#L283-L288 to see how they were used (app.routes). I also have an old tutorial on this here: https://github.com/plotly/dash-docs/blob/master/tutorial/urls.py. Again, this functionality is likely to change which is why I haven’t documented it publically.

Dash has the capabilities to render websites as “single-page-apps” meaning that a full page refresh is not performed when you visit new pages. This will make navigation in the website extremely quick. Instead of reloading the page, the children property of some container element just changes values. For example:

app.layout = html.Div([
    dcc.RadioItems(options=[
        {'label': i, 'value': i} for i in ['Chapter 1', 'Chapter 2']
    ], value='Chapter 1',
    id='navigation-links'),
    html.Div(id='body')
])
@app.callback(Output('body', 'children'), [Input('navigation-links', 'value')])
def display_children(chapter):
    if chapter == 'Chapter 1':
        return Div('Welcome to Chapter 1')
    elif chapter == 'Chapter 2':
        return Div('Welcome to Chapter 2')
```

This example hides and shows content when you click on different radio items. Instead of a `RadioItems` component, I'd like to add a `URLBar` component that contains state of the current address bar. This component could be used as an input to hide and show content depending on what URL you're on. Another component, like a `Tab` component or that `RadioItems` component could act as a table of contents and update the `URLBar` component when you change tabs. 

That's the idea at least. There is a little bit more work to make this happen, but it's on my immediate roadmap.

#3

That’s great that you have this on your immediate roadmap. Glad to hear it.

My first thought is that it could become unwieldy to have a singular, all-inclusive app.layout section written into the app.py document.
I find that it helps me keep organized to have pagename.html files in the /templates folder nested on the folder where I keep app.py, main.py, wsgi.py, et cetera.

Thoughts on how you might break-out / compartmentalize the app.layout?
I am also thinking about compatibility with Flask Bootstrap / Jinja2 templates…


#4

My first thought is that it could become unwieldy to have a singular, all-inclusive app.layout section written into the app.py document.
I find that it helps me keep organized to have pagename.html files in the /templates folder nested on the folder where I keep app.py, main.py, wsgi.py, et cetera.

Thoughts on how you might break-out / compartmentalize the app.layout?

Yeah, you can always just import layouts from separate files. Take a look at the organization of the docs here: https://github.com/plotly/dash-docs/tree/master/tutorial. Each file basically represents a separate “page”. A central run.py file collects the URLs and has the hiding and showing logic.

I am also thinking about compatibility with Flask Bootstrap / Jinja2 templates…

For large existing sites that happen to made in Flask, I’m recommending that Dash is just run along side those apps and that the apps are embedded into the existing site using <iframe/>s (for now!). Dash itself won’t provide compatibility with Jinja templates. For CSS, you could embed the stylesheets inside the dash app examples too.


#5

Some updates:

  • I just wrote a Link and Location component that could be used for creating “single page apps”. Here is the issue on GitHub (please provide feedback there!). It’s in the pre-release channel right now but will be added to the core library within the week.
  • There is a community PR to add multiple apps with a base URL here: https://github.com/plotly/dash/pull/70

#6

Hi there,

How would one go about embedding a Dash app in an iframe? I’m pretty new to Plotly, Dash, and Python in general, so maybe this is obvious to other users. I’ve created a Flask app with several Plotly graphs, and when Dash was released it seemed like a great tool to use for some additional content that I had been thinking about creating. The Dash app works great on its own, however, I’d really like to get it into my overall Flask app.

Thanks,
Nick


#7

I believe what you are asking is not specific to dash. Embedding dash apps is like embedding anything else:

<iframe src="http://youdashapp.company.com/product1_kpis" scrolling="no" width="1200" height="2000" frameBorder="0"></iframe>


#8

Related to the original question, in Flask you can do things like

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % username

How do you do something similar in Dash, so that I can retrieve the value username and then plot something that depends on that value?

Thanks


#9

Ok. So I would basically have to have the Dash app running elsewhere with it’s own URL. I can’t just have the Dashapp.py sitting in my current Flask app folder, and somehow “run” the Dash app from the iframe.

Cheers,
Nick


#10

Updates!

You can now create a multi-page Dash app using links and the new dash_core_components.Location component. I just wrote a tutorial on this here: https://plot.ly/dash/urls. This is what the multi-page Dash user guide (https://plot.ly/dash, github.com/plotly/dash-docs) uses.


#11

To embed the app as an Iframe, that is correct.


#12

This may not be exactly what you asked, didac but its working for me. The new Location object lets you scrape parameters from a URL only you have to encode it in the function. Then use those parameters and a callback to create content dynamically through app.layout.

app.layout = html.Div([ dcc.Location(id='url', refresh=False),                                     
                                     html.Div(id='page-content')                                                    
                                    ]
                                   )

@app.callback(Output('page-content', 'children'),                                                      
                       [Input('url', 'pathname')])                                                         
def generate_layout(url):
     """ url:  not a full url, but just the part after the host ip and port.  
                  ex:  [full url] http://my-dash-server:8080/username=duncan&userid=1234&graphsettings=23]
                         [actual url string:   /username=duncan&userid ... ]
     """                                                                                 
     if not url: url='/'                                                                                   
     config = dict(urllib.parse.parse_qsl(url.strip('/')))
     ### alternative way to parse url
     #config = urllib.parse.parse_qs(url)                                                           

     return generate_content(config)

def generate_content(config):
    """ Uses config dictionary as parameters to generate a list of html or dcc components """
    return []

#14

A post was split to a new topic: URL Not Found - Multi-Page Dash App