gradio-app / gradio

Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work!
http://www.gradio.app
Apache License 2.0
34.13k stars 2.6k forks source link

JavaScript in button.click() seems to not work as intended #9454

Open dechantoine opened 1 month ago

dechantoine commented 1 month ago

Describe the bug

Hi ! I try to run a js script on button click.

To keep it simple for the issue, the script should display a string which correspond to the value of a dropdown. For my true app, I need to store the current value of the dropdown into a State.

When this state is fed into button.click(), I have 2 different behaviors :

Did I missed something or is this a bug ?

Have you searched existing issues? 🔎

Reproduction

import gradio as gr
import logging

logger = logging.getLogger(__name__)

samples = {
    "#1": "first_sample",
    "#2": "second_sample",
}

html = """
<html>
  <body>
    <h1 style="text-align: center;">Your string</h1>
    <div id="text" style="width: 100%; height: 800px; margin: 0 auto; display: block;"></div>
  </body>
</html>
"""

scripts = """
async (str) => {
  console.log("Input string:", str);
  document.getElementById('text').innerHTML = str;
}
"""

with gr.Blocks() as demo:
    with gr.Tab("Start building"):
        with gr.Column():
            # Block layout
            string = gr.State(value="first_sample")

            dropdown = gr.Dropdown(choices=list(samples.keys()),
                                   label="Select input string",
                                   value="#1")
            button_load = gr.Button(value="Test JS")

            html_output = gr.HTML(html)

            # Events
            dropdown.change(fn=lambda x: samples[x],
                            inputs=dropdown,
                            outputs=string)

            # Log the string in the Gradio state
            string.change(fn=lambda x: logger.info(f"input type: {type(x)}, change : {x}"),
                          inputs=string,
                          outputs=None)

            # Load the string
            button_load.click(fn=lambda x: html,
                              inputs=string,
                              outputs=html_output,
                              js=scripts)

if __name__ == "__main__":
    demo.launch(debug=True,
                show_error=True)

Screenshot

No response

Logs

Input string: first_sample
Input string: null

System Info

Gradio Environment Information:
------------------------------
Operating System: Darwin
gradio version: 4.44.0
gradio_client version: 1.3.0

------------------------------------------------
gradio dependencies in your environment:

aiofiles: 23.2.1
anyio: 4.6.0
fastapi: 0.115.0
ffmpy: 0.4.0
gradio-client==1.3.0 is not installed.
httpx: 0.27.2
huggingface-hub: 0.25.1
importlib-resources: 6.4.5
jinja2: 3.1.4
markupsafe: 2.1.5
matplotlib: 3.9.2
numpy: 2.1.1
orjson: 3.10.7
packaging: 24.1
pandas: 2.2.3
pillow: 10.4.0
pydantic: 2.9.2
pydub: 0.25.1
python-multipart: 0.0.10
pyyaml: 6.0.2
ruff: 0.6.7
semantic-version: 2.10.0
tomlkit==0.12.0 is not installed.
typer: 0.12.5
typing-extensions: 4.12.2
urllib3: 2.2.3
uvicorn: 0.30.6
authlib; extra == 'oauth' is not installed.
itsdangerous; extra == 'oauth' is not installed.

gradio_client dependencies in your environment:

fsspec: 2024.9.0
httpx: 0.27.2
huggingface-hub: 0.25.1
packaging: 24.1
typing-extensions: 4.12.2
websockets: 12.0

Severity

Blocking usage of gradio

dechantoine commented 1 month ago

If anyone wondered, I was able to find a dirty fix by storing my value in a non-visible Textbox instead of a State :

import gradio as gr
import logging

logger = logging.getLogger(__name__)

samples = {
    "#1": "first_sample",
    "#2": "second_sample",
}

html = """
<html>
  <body>
    <h1 style="text-align: center;">Your string</h1>
    <div id="text" style="width: 100%; height: 800px; margin: 0 auto; display: block;"></div>
  </body>
</html>
"""

scripts = """
async (string) => {
  const text_str = document.getElementById("text_box").getElementsByTagName('textarea')[0].value;
  console.log("Input string:", text_str);
  document.getElementById('text').innerHTML = text_str;
}
"""

with gr.Blocks() as demo:
    with gr.Tab("Start building"):
        with gr.Column():
            # Block layout
            text_box = gr.Textbox(label="Input string",
                                  value="first_sample",
                                  elem_id="text_box",
                                  interactive=False,
                                  visible=False)

            dropdown = gr.Dropdown(choices=list(samples.keys()),
                                   label="Select input string",
                                   value="#1")

            button_load = gr.Button(value="Test JS")

            html_output = gr.HTML(html)

            # Events
            dropdown.change(fn=lambda x: samples[x],
                            inputs=dropdown,
                            outputs=text_box)

            # Load the string
            button_load.click(fn=lambda: html,
                              inputs=None,
                              outputs=html_output,
                              js=scripts)

if __name__ == "__main__":
    demo.launch(debug=True,
                show_error=True)