You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Right now the rendering of the dash index is done with a simple string formatting.
This is not ideal, flask is shipped with jinja2 and we should use it to render the
dash index page instead.
Pros:
Easier to change.
Easier to read.
Easily expandable.
Safer formatting.
Extendable by the user.
Context variables.
Blocks
Cons:
Slower ?
Here's the template I've come up with on branch jinja-index-render:
I'm having a bit of trouble understanding the "pros" here... Is it mostly that it would be easier for people who already use Jinja elsewhere to reuse bits of those apps? Because this is just rendered one time, without any conditional variables being injected etc (other than JS and CSS lists)..
I don't really see the pros here also. index.html is just a skeleton html that is needed to render the initial page the React app is injected to. I don't see it as being a common site of customisation, which is why the manual override method proposed in #265 is an advanced feature, intended for use by people with unusual requirements. So it doesn't seem like a use-case that we should be optimising for developer ergonomics (which seems to be the main benefit of using Jinja2 templates).
The big downside that is not mentioned is that this introduces a functional dependency on Jinja2. Even though Jinja2 is bundled with Flask, it's not used by default. In addition to introducing this dependency, we'd now also require people to learn yet another markup language, as opposed to vanilla HTML.
Currently, there's nothing stopping someone subclassing the Dash class and using Jinja2 to compile the HTML if they wanted the convenience. This could be called out as a mode of use that the manual override method from #265 should support?
I would also argue that, if this is our proposed solution in the docs, could create confusion about the use of templates (templates implying more than one template), while one of the great things about Dash is that you can write single-page applications in Python. Not that you have to use templates of course, but I would stay away from using templates in official documentation, just to avoid confusion!
(perhaps this isn't really a distinct issue from #265... I'm having trouble picking apart which bits of my feedback go in which of the two issues :)
I also don't like the templates directory because it's plural: it induces people to ask "what other templates could/should I have in there?". I would favour an implicit default path personally (as mentioned in #265)
The biggest problem that I see here is that this proposal seems to reduce the difficulty of very-advanced use cases at the expense of raising the complexity of very-basic use cases, by forcing people to learn Jinja as opposed to the string interpolation suggested in #265. This isn't a deal breaker for me but it's something to think about.
The templates with plural is just the way it is with default flask. I also think as since it's just single file we want to override it would be better to just look for it in the project root.
Thanks to everyone who is providing feedback on this one :)
I think that I'm still in favor of the interpolated string option:
I agree with @ned2 's point about requiring another concept for users to learn.
Some users in the community wish that Dash was entirely Jinja based (e.g. HTML5 templates? #44) and so I feel like it's easier if we have clear separation between Dash and Jinja
For me, I think the main reason that I prefer the interpolated string method is that I find it to be more transparent in three areas:
Am I overriding existing content or adding new content?
What's the order of the tags?
What's included by default?
Here are some use cases that I have in mind:
When I want to include custom CSS, sometimes I want that CSS to be included before the component's CSS, sometimes after, and sometimes I want to replace the component's CSS. By reading the index string, it's easy for me to inject the different strings where I want it. If I used the templates, I might start by setting {% block css %} but then I wouldn't have control over the placement of that block with respect to the auto-included CSS
There have been various requests by users to add various elements to the head:
In those cases, if I used {% block meta %}, it wouldn't be clear to me if I was adding new tags or overriding the existing tags, and both use cases are valid.
With the index string method, it's very transparent to me what I would be overriding vs extending and which order the tags would be placed in.
And while I like how simple something like this looks:
I feel like in order to understand that, I'd need knowledge of:
First, basic jinja concepts (extends and block)
Then, which blocks are available (e.g. header) and where the fit into the underlying HTML
Whether what I need to do can be simply included in a block header or whether I need to provide a larger index.html
Since our index string is so simple and readable, I feel like it's just as much work to read the index string and see exactly where the different blocks go.
In response to some other design decisions:
I agree that users should not have to create an templates folder and that we should either look in the (configurable) static folder or in the root folder
While we can make static configurable, I think we should focus on a standard way to do it (i.e. de-emphasize that it's configurable). This is the ruby on rails way to do things and I think it makes our communication easier (e.g. in the forums, we can universally say "put it in the 'static' folder")
In addition to a file, I think that users should be able to just pass in a string without creating a separate file
If we automatically include CSS and JS that's in a static folder, users that are already including CSS might have their CSS included twice when they upgrade. We could get around this by:
Ignoring those users. There probably aren't many of them and they should be reading the CHANGELOG.md anyway. (I don't necessarily agree with this.)
Making our new preferred folder assets instead of static and start auto-including CSS by default only in that folder. "assets" becomes the new "static" and we start using that language everywhere.
For what it's worth, here's what I did to build a Dash template from an existing Flask, Jinja template.
In case you're wondering, I have a hybrid web app that uses both Flask and Dash.
First extend your "base.html" template into a Dash-specific template (e.g. "base_dash.html"), using HTML comments for the things Dash needs to replace (e.g. {%metas%} or {%app_entry%}:
dashapp=dash.Dash()
# FYI, you need both an app context and a request context to use url_for() in the Jinja2 templateswithapp.app_context(), app.test_request_context():
layout_dash=pathlib.Path(get_root_path(__name__)).joinpath("templates").joinpath("base_dash.html")
withopen(layout_dash, "r") asf:
html_body=render_template_string(f.read())
comments_to_replace= ("metas", "title", "favicon", "css", "app_entry", "config", "scripts", "renderer")
forcommentincomments_to_replace:
html_body=html_body.replace(f"<!-- {comment} -->", "{%"+comment+"%}")
dashapp.index_string=html_body
Reacted by zaratustra, Alberto Fanjul and AffanhKhan
Activity
nicolaskruchten commentedon Jul 5, 2018
I'm having a bit of trouble understanding the "pros" here... Is it mostly that it would be easier for people who already use Jinja elsewhere to reuse bits of those apps? Because this is just rendered one time, without any conditional variables being injected etc (other than JS and CSS lists)..
ned2 commentedon Jul 6, 2018
I don't really see the pros here also.
index.htmlis just a skeleton html that is needed to render the initial page the React app is injected to. I don't see it as being a common site of customisation, which is why the manual override method proposed in #265 is an advanced feature, intended for use by people with unusual requirements. So it doesn't seem like a use-case that we should be optimising for developer ergonomics (which seems to be the main benefit of using Jinja2 templates).The big downside that is not mentioned is that this introduces a functional dependency on Jinja2. Even though Jinja2 is bundled with Flask, it's not used by default. In addition to introducing this dependency, we'd now also require people to learn yet another markup language, as opposed to vanilla HTML.
Currently, there's nothing stopping someone subclassing the
Dashclass and using Jinja2 to compile the HTML if they wanted the convenience. This could be called out as a mode of use that the manual override method from #265 should support?valentijnnieman commentedon Jul 6, 2018
I would also argue that, if this is our proposed solution in the docs, could create confusion about the use of
templates(templates implying more than one template), while one of the great things about Dash is that you can write single-page applications in Python. Not that you have to use templates of course, but I would stay away from using templates in official documentation, just to avoid confusion!nicolaskruchten commentedon Jul 6, 2018
(perhaps this isn't really a distinct issue from #265... I'm having trouble picking apart which bits of my feedback go in which of the two issues :)
I also don't like the
templatesdirectory because it's plural: it induces people to ask "what other templates could/should I have in there?". I would favour an implicit default path personally (as mentioned in #265)The biggest problem that I see here is that this proposal seems to reduce the difficulty of very-advanced use cases at the expense of raising the complexity of very-basic use cases, by forcing people to learn Jinja as opposed to the string interpolation suggested in #265. This isn't a deal breaker for me but it's something to think about.
nicolaskruchten commentedon Jul 6, 2018
(sorry, I definitely didn't mean to close the issue, I mis-clicked!)
T4rk1n commentedon Jul 6, 2018
The
templateswith plural is just the way it is with default flask. I also think as since it's just single file we want to override it would be better to just look for it in the project root.chriddyp commentedon Jul 9, 2018
Thanks to everyone who is providing feedback on this one :)
I think that I'm still in favor of the interpolated string option:
For me, I think the main reason that I prefer the interpolated string method is that I find it to be more transparent in three areas:
Here are some use cases that I have in mind:
When I want to include custom CSS, sometimes I want that CSS to be included before the component's CSS, sometimes after, and sometimes I want to replace the component's CSS. By reading the index string, it's easy for me to inject the different strings where I want it. If I used the templates, I might start by setting
{% block css %}but then I wouldn't have control over the placement of that block with respect to the auto-included CSSThere have been various requests by users to add various elements to the head:
<meta http-equiv="X-UA-Compatible" content="IE=edge">) Improves cross-browser compatibility #275In those cases, if I used
{% block meta %}, it wouldn't be clear to me if I was adding new tags or overriding the existing tags, and both use cases are valid.With the index string method, it's very transparent to me what I would be overriding vs extending and which order the tags would be placed in.
And while I like how simple something like this looks:
I feel like in order to understand that, I'd need knowledge of:
extendsandblock)blocks are available (e.g.header) and where the fit into the underlying HTMLblockheader or whether I need to provide a largerindex.htmlSince our index string is so simple and readable, I feel like it's just as much work to read the index string and see exactly where the different blocks go.
In response to some other design decisions:
templatesfolder and that we should either look in the (configurable)staticfolder or in the root folderstaticconfigurable, I think we should focus on a standard way to do it (i.e. de-emphasize that it's configurable). This is the ruby on rails way to do things and I think it makes our communication easier (e.g. in the forums, we can universally say "put it in the 'static' folder")staticfolder, users that are already including CSS might have their CSS included twice when they upgrade. We could get around this by:assetsinstead ofstaticand start auto-including CSS by default only in that folder. "assets" becomes the new "static" and we start using that language everywhere.Issue 280 - Refactor cell content (plotly#281)
Issue 280 - Refactor cell content (#281)
mccarthysean commentedon Nov 5, 2021
For what it's worth, here's what I did to build a Dash template from an existing Flask, Jinja template.
In case you're wondering, I have a hybrid web app that uses both Flask and Dash.
First extend your "base.html" template into a Dash-specific template (e.g. "base_dash.html"), using HTML comments for the things Dash needs to replace (e.g.
{%metas%}or{%app_entry%}:{% extends "base.html" %} {% block head %} <!-- metas --> <title> <!-- title --> </title> <!-- favicon --> <!-- css --> {% endblock %} {% block body %} <!-- app_entry --> <footer> <!-- config --> <!-- scripts --> <!-- renderer --> </footer> {% endblock %}Back in Python, when setting up the Dash app: