Using dropzone with ReactiveHTML

I followed the discussion at

and was able to create a drag and drop component using pn.pane.HTML. However, I need the rest of the page to react to files being dropped here. So I tried this

class DragAndDrop(pn.reactive.ReactiveHTML):

    _template = """
        <form action="/upload" class="dropzone" id="demo-upload" style="overflow:auto; width:800px; height:250px">
            <div class="dz-message">
                <button type="button" class="dz-button">Drop files here or click to upload.</button>
                <span >(This is just a demo dropzone. Selected files are <strong>not</strong>
                    actually
                    uploaded.)</span>
            </div>
        </form>
    """
    _scripts = {
        'render' : """
            var myDropzone = new Dropzone("form#demo-upload");
  """
}

And the drop zone is not working. The browser just downloads/opens my files when files are dropped.

This is the error in my console

Error rendering Bokeh items: Error: Invalid dropzone element.
    at new i (dropzone.min.js:1)
    at eval (eval at _render_script (panel.min.js:147), <anonymous>:8:18)
    at g.run_script (panel.min.js:147)
    at g.after_layout (panel.min.js:147)
    at g.compute_layout (panel.min.js:147)
    at g.build (bokeh.min.js?v=ada5d64938a63c435e537bbd83d34dfd3b1da9bfb3ca9c7b8e947f29983f2bd491f6a157b4613e727b5739396d69da58a21a56167d248c5ff99c5f3062545d11:506)
    at g.renderTo (bokeh.min.js?v=ada5d64938a63c435e537bbd83d34dfd3b1da9bfb3ca9c7b8e947f29983f2bd491f6a157b4613e727b5739396d69da58a21a56167d248c5ff99c5f3062545d11:506)
    at f (bokeh.min.js?v=ada5d64938a63c435e537bbd83d34dfd3b1da9bfb3ca9c7b8e947f29983f2bd491f6a157b4613e727b5739396d69da58a21a56167d248c5ff99c5f3062545d11:582)
    at async Object.t.add_document_standalone (bokeh.min.js?v=ada5d64938a63c435e537bbd83d34dfd3b1da9bfb3ca9c7b8e947f29983f2bd491f6a157b4613e727b5739396d69da58a21a56167d248c5ff99c5f3062545d11:582)
    at async w (bokeh.min.js?v=ada5d64938a63c435e537bbd83d34dfd3b1da9bfb3ca9c7b8e947f29983f2bd491f6a157b4613e727b5739396d69da58a21a56167d248c5ff99c5f3062545d11:163)

I tried both render and after_layout hooks for the scripts, any suggestions to either

  1. Let the rest of the page know when something is dropped into the HTML component.
    or
  2. Get ReactiveHTML to work?

Thanks!

I was able to solve it!

Turns out that Panel was changing the id of the dropzone element to form#demo-upload-1002! Once I hard coded this, it works. So, this works for now, but it sure is a bug / needs enhancement.

1 Like

Hi @sandhya-sago

Great you succeeded. Could you share the working script?

The ReactiveHTML is new and dropzone.js Is amazing so the community could learn a lot.

Feel free to share screenshots or gifs as well.

Thanks.

it is not a bug, when you have several elements you need several id numbers. Then, i think the number is added for that case. Panel adds the object to the scope of the created element. Here below you can find the drag and drop without hardcoding the id.

import panel as pn

pn.config.js_files["dropzonejs"]="https://rawgit.com/enyo/dropzone/master/dist/dropzone.js"
pn.config.css_files.append("https://rawgit.com/enyo/dropzone/master/dist/dropzone.css")
pn.config.css_files.append("https://www.dropzonejs.com/css/style.css?v=1595510599")
pn.extension()


class DragAndDrop(pn.reactive.ReactiveHTML):

    _template = """
        <form action="/upload" class="dropzone" id="demoUpload" style="overflow:auto; width:800px; height:250px">
            <div class="dz-message">
                <button type="button" class="dz-button">Drop files here or click to upload.</button>
                <span >(This is just a demo dropzone. Selected files are <strong>not</strong>
                    actually
                    uploaded.)</span>
            </div>
        </form>
    """
    _scripts = {
        'render' : """
        console.log(self, demoUpload.id, data.value);
            var myDropzone = new Dropzone("form#" + demoUpload.id);
            

  """
}

DragAndDrop().servable()

2 Likes

Thanks a bunch, this cleaned up a lot! I wonder where this is documented, I couldn’t find it.

1 Like

Hi @sandhya-sago

Did you manage to get it working? Could you share a small example? Thanks.

Hi @sandhya-sago @Marc

Do you know if anyone has gotten it to work? I have implemented the dropzone but I can’t connect to python to save the files. Do you have an example that you can share?

Hi @Gustamobe

Welcome to the community.

I experimented a bit with it now. And here is a POC demonstrating that it can work

dropzone-js

import panel as pn
import param

pn.extension()

CSS = """
.dropzone {
  text-align: center;
  padding: 20px;
  border: 3px dashed #eeeeee;
  background-color: #fafafa;
  color: #bdbdbd;

  margin-bottom: 20px;
}
"""


class DragAndDrop(pn.reactive.ReactiveHTML):
    data_url = param.String()

    __javascript__ = [
        'https://unpkg.com/dropzone@5/dist/min/dropzone.min.js'
    ]
    
    __css__ = [
        'https://unpkg.com/dropzone@5/dist/min/dropzone.min.css'
    ]

    _template = f"""
        <style>{CSS}</style>
        <form action="/dont-upload" class="dropzone" id="demoUpload" style="overflow:auto; width:800px; height:250px">
            <div class="dz-message">
                <span>📂</span><br></br>
                <button type="button" class="dz-button">Drop files here or click to upload.</button>
            </div>
        </form>
    """
    _scripts = {
        'render' : """
        var myDropzone = new Dropzone("form#" + demoUpload.id, {autoProcessQueue: false});
        myDropzone.on("addedfile", file => {
            const fileReader = new FileReader();
            fileReader.onload = function () {
                data.data_url = fileReader.result;
            };
            fileReader.readAsDataURL(file);
            
        });
  """
}

drag_drop = DragAndDrop()

@pn.depends(drag_drop.param.data_url, watch=True)
def print_added(_):
    print("The data_url is: ", drag_drop.data_url[0:150], "...")


pn.Column(
    drag_drop
).servable()

But dropzone.js is a complicated widget and before you get to a solution for your use case you will have to study the dropzone.js documentation and figure out you want your flow, which events to react to, how to style the widget, how to convert the data_url into a file on the python side and much more.

One place to find inspiration regarding the data_url is in Panels implementation of the FileInput widget.