I want to append a javascript file which makes some changes in the DOM generated by Dash. I get a not encountered error if I simply use app.scripts.append_script. I’ve read this in SO and this in this forum.
I have finally opted for using a setInterval workaround, but Itd be nice if Dash provided a way to assign callbacks when the DOM is effectively loaded.
I’m trying to make a hide content / show content button. I’m just using raw javascript to assign a toggle hide/show function to the button. The javscript code is here (WIP): https://github.com/Grasia/WikiChron/blob/master/js/app.js
Maybe there is a better way. I didn’t want to break my head in this.
@nedned, it’d be probably much verbose and I’d need at least two callbacks since I also want to change the style of the button, but that might work.
Still looking forward for a hook where we can set javascript code that will be triggered when the actual Dash react components are loaded.
Thank you.
I have run into similar issues and have also used a setTimeout/setInterval hack. My dash app is in an iframe and needs to send information back to the parent domain. I have managed to get this to work but it isn’t elegant. The code below is not complete, it only contains the pieces necessary to communicate back to the parent domain. The comments should help you to follow what is going on.
def write_to_data_uri(s):
"""
Writes to a uri.
Use this function to embed javascript into the dash app.
Adapted from the suggestion by user 'mccalluc' found here:
https://community.plotly.com/t/problem-of-linking-local-javascript-file/6955/2
"""
uri = (
('data:;base64,').encode('utf8') +
urlsafe_b64encode(s.encode('utf8'))
).decode("utf-8", "strict")
return uri
# Get parentDomain
# Define JS func "sendMessageToParent"
app.scripts.append_script({
'external_url': write_to_data_uri("""
var parentDomain = '';
window.addEventListener('message',function(event) {
console.log('message received from parent: ' + event.data,event);
parentDomain = event.data;
},false);
function sendMessageToParent(s) {
console.log("Sending message to parent at ("+parentDomain+") s=" + s);
parent.postMessage(s,parentDomain);
}
""")})
# Hidden div to store the message/string which will be sent to the parent domain
# using 'title' to store the message/string because it is easy to get javascript
# funciton to retrieve the value of the title
html.Div(
id='msg_for_parent',
title='',
style={'display': 'none'}
),
# Button to execute javascript. Does nothing in the dash world
html.Button(
'Click Me',
id='exec_js',
),
# call back to update the title of the hidden div, i.e. update the message for
# the parent domain
# using 'url' as an input but any dash component will do.
@app.callback(
dash.dependencies.Output('msg_for_parent', 'title'),
[dash.dependencies.Input('url', 'pathname')]
)
def update_msg_for_parent(pathname):
msg = "hello from %s" % pathname
return msg
# Javascript to message the parent. Or, in more detail, to send the 'title' of
# the div 'msg_for_parent' to the parent domain.
#
# setTimeout is a dirty, dirty hack. Forces the page to wait two seconds before
# appending this script, by which time "msg_for_parent" and "exec_js" are
# defined. Without this, there is an error to the effect of "cannot set property
# onclick of null"
app.scripts.append_script({
'external_url': write_to_data_uri("""
setTimeout(function(){
$(document).ready(function(){
document.getElementById("exec_js").onclick = function(){
var msg = document.getElementById("msg_for_parent").title;
sendMessageToParent(msg);
};
});
}, 2000);
""")})
Now, you can use a component called that is available in the grasia-dash-components library (pip install grasia-dash-components) and set the src attribute to the url of the external javascript code you want to append. Below I show you a small example where I import the jQuery library:
import dash
import dash_html_components as html
import grasia-dash-components as gdc
app = dash.Dash()
app.layout = html.P('Hello world!')
app.layout += gdc.Import(src="https://code.jquery.com/jquery-3.3.1.min.js")
The Import component will load only when all the dash app is loaded and it isn’t affected by the app.scripts.config.serve_locally setting.
Similarly, you can import local .js files as long as you are serving them from a local web server (You might use the Flask server that Dash uses behind the hoods. Ping me if you need more help on this.)
Technical explanation:
The solution consists in using a react component called that which belongs to an external dash library, and, therefore is loaded only when all the React components for the Dash app are loaded.
This component renders as a html tag with the defer attribute set to true.
The source code is available in Github.
Please, try it and let me know any issues you face.
Your library appears to be what I’m looking for! But when I try to install it with pip I get the following:
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Users\Work\AppData\Local\Temp\pip-install-gv_jvz0l\grasia-dash-co mponents\setup.py", line 5, in <module>
with open('long_description.md') as f:
FileNotFoundError: [Errno 2] No such file or directory: 'long_description.md '
----------------------------------------
Command “python setup.py egg_info” failed with error code 1 in C:\Users\Work\App Data\Local\Temp\pip-install-gv_jvz0l\grasia-dash-components\
I tried to implement your above code, but I get the below error message:
TypeError: unsupported operand type(s) for +=: ‘P’ and ‘Import’
I have a callback in my app that produces a table. I suppose I need to load jquery after this callback, because it appears without any style when produced by callback, but gets styled when inserted by hand into the html layout.
I wanted to try the above approach but am already failing at the beginning due to the TypeError.
Hey Abeserra. I’ve tried using the library to import a javascript file from a server running on localhost and it doesn’t work. I would love to contact you to maybe see what happened. Thank you!
Hi @itamag. Sorry for the late reply. I’ve been very busy with other stuff within the past weeks.
So, have you made sure that you can load that js outside Dash? In other words, can you go to localhost/path/to/file.js and you can see it?
Including custom CSS or JavaScript in your Dash apps is simple. Just create a folder named assets in the root of your app directory and include your CSS and JavaScript files in that folder.
However, keep in mind that with that you can’t choose whether to load or not some js depending on your code or when to load it (i.e. you want to load some js only when a callback is triggered); all the js code located in assets will get loaded at once along with the Dash app.
In case you want to do load the js code in a more discretionary way you might want to use our auxiliar component: gdc.Import()
Since this still shows up as part of the google search for this feature:
I’ve had trouble upgrading to 1.0.0 since my code relied on dash-grasia-components.Import, but it looks like the current version of it is not compatible with Dash 1.0.0 (see related github issue).
I’ve decided to fix this by making a standalone component that pulls out the Import function from dash-grasia-components. I’ve published it to npm as well as pypi so anyone can use pip to install it: