Internal error using custom HTML

@stefano.menci @meredydd Hi Stefano,

Using the “Custom HTML form” I can set an html property to display an html file. This works for some html files, but apparently not for others. For example, this html file which shows an interactive chart just produces an internal error. Any help is appreciated.

It appears that I get an internal error every time one of these interactive charts are present in the html file.

https://drive.google.com/open?id=1ucMsfj66ciVHLY6n_H6WKVAcUjyPVYrk

my_bytes=app_tables.html.get(name='html_file')['file'].get_bytes()
self.custom_html_page.html=my_bytes

Have you tried to print(my_bytes) before assigning it to the custom form?

This will tell you whether the crash is on the first or second line, and, if on the second one, what is the content of my_html.

Yes and this spits out all the html to the output console. Is this what you mean?

Hi guys,

This looks like a bug report! You should get a more useful error than that, at least :slight_smile: I’ve moved these posts into a separate thread in the Bug Reports section.

Can you share an example of an app that causes this issue?

@meredydd Sure thing here it is.

https://anvil.works/ide#clone:N765YJPUBDF6RR5C=4MP5HS66X45TK3AVDUSIFGJ3

Thanks; let me take a look tomorrow (it’s late here!)

No problem. I appreciate it.

@meredydd Sorry to bug you but do you have an update on this? I can provide more information about what I am trying to do if it would help. Feel free to tell me to wait patiently :wink:

Update: Essentially, due to edge cases in the way that the browser loads Javascript, and the workarounds libraries use to get around them, it’s as though every <script> tag has the async attribute. That is to say, scripts will be run in whatever order they are loaded, even if one is after the other in the document. This means that some standard library loading techniques will not work:

<script src="https://somewhere/my-library.js"></script>
<script>
MyLibrary.doSomething();
// ^-- this might fail, even though my-library.js is loaded earlier in the document,
// because it's loaded asynchronously
</script>

This is undesirable behaviour, and we are working on a reasonable workaround or (ideally) a fix. We’ll keep you posted!


In the meantime, the following rather awkward workaround does work: If you dynamically create a <script> tag to load the external library, you can set a function as an onload handler, which runs after the library has loaded:

<script>
  function doTheThingOnceTheLibraryHasLoaded() {
    // Do what you wanted to do here, eg:
    MyLibrary.doSomething(...);
  }
  var s = document.createElement("script");
  s.onload = doTheThingOnceTheLibraryHasLoaded;
  s.setAttribute("src", 'https://somewhere/my-library.js');
  var head = document.getElementsByTagName('head').item(0);
  head.appendChild(s);
</script>

As I say, this is rather cumbersome, and we’re working on something better - but hopefully this will get you going for now!

Thanks meredydd. I will do my best to follow the instructions here. Thanks for the update.

@meredydd Okay I have followed the instructions and to my surprise I can actually get the html file to appear, complete with the interactive visualization. I say it is a surprise becasue I’m completely green when it comes to html and js libraries. I have a few questions though:

(1) For some reason I have to ask Anvil to display the html twice before it will show on the screen (see cloned app below) (the first request is met with an internal error).

(2) The thing I don’t understand is that at the beginning of the original html file, three libraries are loaded.

<script src="https://cdn.jsdelivr.net/npm//vega@3.3.1"></script>
<script src="https://cdn.jsdelivr.net/npm//vega-lite@2.6.0"></script>
<script src="https://cdn.jsdelivr.net/npm//vega-embed@3.14"></script>

In your chunk of code I can set the src to a single library. Example:

 s.setAttribute("src", 'https://somewhere/my-library.js');

I can replace 'https://somewhere/my-library.js' with any one of the three libraries above, and they all seem to work. I realize that you may not be familiar with these libraries, but in your opinion should I just use your work-around as is, or is there something more I should be doing given that there are three libraries?

(3) These html files are generated for me using the Altair visualization library (which provides a python API for making vega-lite statistical charts). So once the html files are generated, I will have to write some additional code to re-jigger the html so that it can be used with Anvil. Would you recommend beautiful soup, or just plain python text editing, or something else to do this processing?

Here is the updated app where you can chose to display the edited html_file:
https://anvil.works/ide#clone:N765YJPUBDF6RR5C=4MP5HS66X45TK3AVDUSIFGJ3

@meredydd I know you are very busy but do you have any suggestions based on my previous comment?

Hi Allan,

If you’ve obtained the whole HTML file from an external source, you might want to try displaying it in an <iframe>. Here’s an example that displays a selection of Media from a data table. I use the Media get_url() method to get a URL to use in the iframe:

https://anvil.works/ide#clone:OSAQYBSLY6NAOZWN=UINJLNRAXW6E4FWDLKMAVK2H


NOTE: Do not use this mechanism to display untrusted HTML! The scripts in these pages are served from the same domain name as the rest of your app, so a script on this page can hijack the rest of your app. In this example, I assume that all the data in your data tables was generated by you, and can’t be supplied by a malicious user.

If you want to display untrusted HTML in an iframe, you’ll want to serve it from an HTTP endpoint in a separate app (which will be on a separate domain, which will give it “cross-origin” protection). Of course then you’ll need another way of ensuring that only authorised people can access those endpoints (probably some kind of secret string in the URL, which you check against a data table).

1 Like

@meredydd Thanks for the advice. I will give it a shot.

@meredydd quick question:

This works amazingly in your app. In mine though I can only get the url to trigger a download, rather than to veiw the HTML on the screen. I am using your IFrame as a custom component and setting it’s url to a url obtained from a file in the datatable:

  def button_1_click(self, **event_args):
       """This method is called when the button is clicked"""
       url=app_tables.html.get(name='html_original')['file'].url
       self.i_frame_1.url=url
      

What am I missing here?
https://35VB7YDXEJZHY3LS.anvil.app/UCKQEBPXWEPZOI6MEUALVFZ6

Okay, I got it. As you said above: use get_url(), and I had to use get_url(False) as one can see in your code from the clone.

Thanks VERY much.

Amazing stuff.

1 Like