Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a pure JavaScript event handler #2536

Merged
merged 6 commits into from
Feb 26, 2024

Conversation

WSH032
Copy link
Contributor

@WSH032 WSH032 commented Feb 11, 2024

@WSH032
Copy link
Contributor Author

WSH032 commented Feb 11, 2024

This PR still has some issues:

  1. Should we use the Function instead of eval?
    Using Function might make the API more complex.
    If we use eval, we should inform users about the security concerns.

  2. The current implementation places all JavaScript event listeners after Python listeners. Perhaps we should preserve the order in which they are added?

nicegui/element.py Outdated Show resolved Hide resolved
@falkoschindler falkoschindler added this to the 1.4.16 milestone Feb 11, 2024
@falkoschindler falkoschindler added the enhancement New feature or request label Feb 11, 2024
Copy link
Contributor

@falkoschindler falkoschindler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this pull request, @WSH032!

Regarding the two issues you mentioned:

  1. I merged both types of listeners, which simplifies the code and preserves their order.
  2. eval seems to be much easier to use than new Function, especially when we want to support event arguments like (e) => alert(e). So I think it is reasonable to keep eval. In the end it's not arbitrary code that is executed, but code defined in a Python file on the server.

We could add another demo to the documentation, showcasing the new js_handler. I'm just not sure about a good place for it.

@falkoschindler falkoschindler marked this pull request as ready for review February 12, 2024 16:16
@WSH032
Copy link
Contributor Author

WSH032 commented Feb 13, 2024

We could add another demo to the documentation, showcasing the new js_handler. I'm just not sure about a good place for it.

I think we can provide a basic introduction to it in generic_events, informing users that we use eval to execute this piece of code, so users need to ensure that the return value after executing the js code is a callable object.

Additionally, some Python event listeners using the ui.run_javascript can be rewritten as JavaScript event listeners to reduce network requests for a better experience.

https://nicegui.io/documentation/run_javascript#run_javascript


My original purpose of using this feature was to design a form that requires calling some browser APIs, such as fetch, upon submission. Here's the simplest example:

from nicegui import ui

js_handler = """\
(evt) => {
    evt.preventDefault();
    localStorage.setItem('password', evt.target[0].value);
    alert(localStorage.getItem('password'));
}
"""

with ui.element("q-form").on("submit", js_handler=js_handler) as form:
    ui.input("Password")
    ui.button("Submit").props("type=submit")


ui.run(reload=True)

@WSH032
Copy link
Contributor Author

WSH032 commented Feb 13, 2024

I just thought of a use case where it seems we can't access the component's this. Is there any solution?

from nicegui import ui

js_handler = """\
(
    function(event) {
        // we expect `this` is the component,
        // but here it's `undefined`.
        console.log(this === undefined)
    }
)
"""

ui.button('button').on("click", js_handler=js_handler)

ui.run(reload=True)

Copy link
Member

@rodja rodja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a short example to the docs. And I wonder if we could make #2377 (comment) work. Could we somehow not go through Python at all? That would enable Safari to allow something like

ui.icon('link').on('click', js_handler='navigator.clipboard.writeText("from nicegui")')

@WSH032
Copy link
Contributor Author

WSH032 commented Feb 19, 2024

@rodja

Could we somehow not go through Python at all?

I'm not sure if I understand your meaning. At least for the current implementation, js event handler should only run on the frontend and do not require communication with the server. In my testing, even when I completely shut down Python, the clipboard still works fine.

Note, we should provide a function as the code

- ui.icon('link').on('click', js_handler='navigator.clipboard.writeText("from nicegui")')
+ ui.icon('link').on('click', js_handler='() => {navigator.clipboard.writeText("from nicegui")}')

@falkoschindler
Copy link
Contributor

I just added a demo to the "Generic Events" section, showing how js_handler can be used for writing to the clipboard. I guess this is what @rodja meant.

Furthermore I made use of this new parameter to simplify the ui.code element as well as the copy-to-clipboard feature for our live demos.

@falkoschindler falkoschindler changed the title feat: javascript event listener Introduce a pure JavaScript event handler Feb 24, 2024
Copy link
Member

@rodja rodja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonderful!

@rodja rodja merged commit bd98fb4 into zauberzeug:main Feb 26, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants