-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Behaviour of py-*: how to pass event #835
Comments
This behavior was changed as part of #686, which was included in the 2022.09.1 release of PyScript which was published last week. If you're linking to The new syntax is: <input
py-change="form_field_changed()"
...
/>
<py-script>
def form_field_changed():
...
</py-script> This change was made to more closely mirror JavaScript's onevent syntax. Note that PyScript's syntax does not currently make the |
ok thank you for the explanation! |
Yes, we need a way to get the event passed cleanly into the function. Looks like an area of work. |
I'm ready to give some help in my free time, where can i look to get an overview of the event handler? |
Thanks @dj-fiorex! That would be swell. The logic for the Perhaps writing up an explanation or reference-guide for the docs section would be the best place for that info to go, if you're game to write it up. @marimeireles may have thoughts as to the best home for that info. For what it's worth, not every possible event is currently captured (though all the 'common' ones are) - #801 has some details. For what it's worth, and slightly orthogonal to your question here, events are available if you add the event listener separately: <!-- py-*event syntax doesn't current pass event nor "this"-->
<button id="one" py-click="say_hello()">No Event with Py-click</button>
<py-script>
from js import console,
def say_hello():
console.log("HELLO!")
</py-script>
<!-- but pyodide.ffi.wrappers.add_event_listener or Element.addEventListener do-->
<button id="two">add_event_listener passes event</button>
<py-script>
from js import console, document
from pyodide.ffi.wrappers import add_event_listener
def hello_args(*args):
console.log(f"Hi! I got some args! {args}")
add_event_listener(document.getElementById("two"), "click", say_goodbye)
</py-script> As @tedpatrick, we should make it possible either way, but if you need that functionality for a project you're working on now, that's a workaround. |
I am working on this at 2 levels:
Case 1: <button py-click="foo()">ClickMe</button> SUPPORTED
Case 2: <button py-click="foo">ClickMe</button> PROPOSED In case 2, if the target function has 1 argument, it gets passed the event. If no arguments, no event. This is an area of risk in terms of API design, I would rather do the simple thing now. We can always get more advanced in time. |
You can also use a create_proxy <html>
<head>
<link rel="stylesheet" href="https://pyscript.net/unstable/pyscript.css" />
<script defer src="https://pyscript.net/unstable/pyscript.js"></script>
</head>
<body>
<button id="two">add_event_listener passes event</button>
<py-script>
from pyodide.ffi import create_proxy
def hello_args(*args):
console.log(f"Hi! I got some args! {args}")
Element('two').element.addEventListener("click", create_proxy(hello_args))
</py-script>
</body>
</html> |
+1 to sometime having a solution that works when attributes are added after initialization - from the amount of times this has come up recently, I suspect PyScript will ultimately be one large mutation observer. And not in a bad way!
-1 to case 2: that was the sole behavior in 2022.06.1, and we specifically moved away from it with PR #686, because it's not how script onevent syntax works, and there was preference (from @fpliger et al) to at least not have PyScript do things that are surprisingly different from JavaScript in these areas. If it's possible: Case 3: <button py-click="foo(event)">ClickMe</button> POSSIBLE?? would be closest, I think, though I have no idea how to implement it. |
That syntax is what several leading JS frameworks do today, it isn't standard but it is the common syntax for assigning attributes function values by name. vue react svelte Much is possible, the key is what interface is best for building apps from a developer's perspective. I personally would use this all the time. And what if it worked depending on the target method's arguments? I believe there is merit in supporting both. <button py-click="foo">ClickMe</button> <button py-click="foo()">ClickMe</button> |
Hey @dj-fiorex, my input for docs is that we may start introducing real content to the Reference part. |
And now for something completely different... In exploring how to address the issues in py-event attributes, I took a detour and ended up somewhere interesting. There are 4 core issues:
API (does not work without framework modifications) <body style="padding:2em;">
<button onclick="py.foo()">call foo on click</button><br><br>
<button onclick="py.boo(event)">call boo on click w event</button><br><br>
<button onclick="py.loo(event, 123)">call loo on click w event + 123</button><br><br>
<button onclick="py.nothing(event)">call nothing on click w event</button><br><br>
<button id="empty_button">empty_button with mousemove event</button><br><br>
<py-script>
import js
@handler
def foo():
js.console.log("foo called")
@handler
def boo(e):
js.console.log("boo called with event")
js.console.log( e )
@handler
def loo(*e):
js.console.log("loo called with *event")
js.console.log( e[0] )
js.console.log( e[1] )
@handler(event="click")
def doc_click(*e):
js.console.log("doc_click called with *event")
js.console.log( e[0] )
@handler( event="mousemove", element=Element("empty_button").element )
def button_mousemove(*e):
js.console.log("button_mousemove called with *event")
js.console.log( e[0] )
</py-script>
</body> This is using the on[event] attribute syntax in JS but it adds in a global
within all on[event] attributes, the As for exposing python methods into the Here we expose the button_mousemove handler to js and subscribe it to the mousemove event for a button element. @handler( event="mousemove", element=Element("empty_button").element )
def button_mousemove(*e):
js.console.log("button_mousemove called with *event") Here is a working version for review. Open the JS console to see this working fully. Source is here. The irony is in exploring py-event, there might be a cleaner, simpler way to manage events within a PyScript application. Note, this is VERY EARLY and VERY POC. If we were to move forward along these lines, the naming and defaults would change but I think it feels right. Feedback welcome. |
i
for solely accessing event the method |
According to Mozilla's docs |
Sure but the window.event on python side is empty, so it's safe to use it for what it was/is used for : passing event for argument-less inline onclick=click() doing onclick=click(event) would imply addressing window.event ... So if it vanishes suddenly after deprecation how one would get the event in old code ? At least window.event is a single place in the proxy to maintain and to adapt to future ways. |
I don't understand. Are you proposing that we should write to
I admit that I am having troubles to understand this sentence of the mozilla docs:
does it mean that you should not use the |
Window.event is deprecated but it has very wide support
<https://developer.mozilla.org/en-US/docs/Web/API/Window/event#browser_compatibility>
and its removal would break the web.
The @handler( event="click" ) decorator uses addEventListener internally so
this essentially aligns with how events work in the DOM Event spec. You can
use on[event] handlers and you get the existing behavior or you can use
addEventListener directly or via the @handler decorator.
on[event] attributes have limitations in that one 1 can exist for a single
event type whereas addEventListener events can have many listeners and
support event options.
In py-event attributes, there is currently no way to obtain the event and
pass it to python. Also, py-event attributes are only set at startup and
lack any runtime flexibility.
The interesting part here is that these are actually independent of
on[event] does not conflict with py-event or vice versa.
…On Thu, Oct 13, 2022 at 6:53 AM Antonio Cuni ***@***.***> wrote:
Sure but the window.event on python side is empty, so it's safe to use it
for what it was/is used for : passing event for argument-less inline
onclick=click()
I don't understand. Are you proposing that we should write to window.event
?
If so, I don't think it's not safe at all. Even on the Python side,
js.window is a reference to the real window object on the JS side, so if
we try to write a .event attribute on it, I'm sure things will explode.
doing onclick=click(event) would imply addressing window.event ... So if
it vanishes suddenly after deprecation how one would get the event in old
code ?
I admit that I am having troubles to understand this sentence of the
mozilla docs:
you should instead use the Event
<https://developer.mozilla.org/en-US/docs/Web/API/Event> passed into the
event handler function.
does it mean that you should not use the onclick=... HTML attribute but
that you have to manually use addEventListener if you want to access the
event?
—
Reply to this email directly, view it on GitHub
<#835 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AISG7AGVFURSNR54DC23TZ3WC7Z4NANCNFSM6AAAAAAQ62ZVEI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
indeed it can have side effects and it's wasted cycle to send it back to js, why not just set "builtins.event" then only on python side ( i updated https://pygame-web.github.io/showroom/pyscript/unify.html accordingly ) ? ps @antocuni: it's "black -x" formatted now, and maybe that's why i wanted json in pyconfig instead of toml ;) |
well, relying on global state seems suboptimal and a certain source of bug. I would strongly prefer a solution in which we pass Example of things which can go wrong with a global/window/builtins def on_click():
assert event.type == "click" # guaranteed to work
await fetch_something()
assert event.type == "click" # might fail if another event handler was fired in the meantime |
@tedpatrick This is a very slick implementation! I agree with your four goals - the only thing that I think is missing is that there's no way to pass Python variables/objects as arguments, true? Since all arguments in that scheme live in JS? Can I suggest some alternate solutions for achieving the same goals? I've re-ordered them for narrative purposes. We need an easy way to subscribe to events in python. We need to support adding/removing these handlers at runtime (py-event is wired up once at startup)I personally think both of these are covered by Pyodide's add_event_listener()/ remove_event_listener(), which are a combination of import pyodide
def button_mousemove(*e):
js.console.log("button_mousemove called with *event")
js.console.log( e[0] )
pyodide.ffi.add_event_listener( Element("empty_button").element, event="mousemove", button_mousemove) Though perhaps having this functionality as a decorator in PyScript might be appealing? If we want to dynamically add this behavior to elements that gain a "py-[event]" attribute, I think a MutationObserver might be the way. We need to get the event object into python for event handlersThis is something I've been noodling on, and have a working prototype of. An example of code would be: <button id="b" py-click="my_python_function(event)">Run my_python_function</button> The key additions are: #pyscript.py
from js import window
from contextvars import ContextVar
event = None
def load_event(js_event):
global event #See below, this will be a ContextVar
event = js_event
window.loadEvent = load_event #See below //pyscript.ts
el.addEventListener(event, (event) => {
event.preventDefault();
(async(event) => {window.loadEvent(event); await runtime.run(handlerCode)})(event); This is just the core of the idea - rather than a global It isn't clear how to expose a python function in JSIf PyScript were to export a reference to the Pyodide interpretter, Python functions would be accessible at something like This has been a request for awhile per that issue, and I think would solve that issue in the same way the Pyodide does. The interesting part here is that these are actually independent of on[event] does not conflict with py-event or vice versa.I was just thinking the same thing! |
Hello guys, i'm a web developer for 90% of my time and 10% python, and i can say that in the web space the event handler receive an event as an argument. |
Small note...I agree with @antocuni about the event argument being deprecated. My IDE is yelling at me when it is used. 😀 Yes it is still implemented. But if it's a bad practice, it's a bad practice. |
Just to be extra sure that we are talking about the same thing: you mean the |
@antocuni Yes. I will generally wear the hat for "Good IDE and tooling integration" and this is one of those places. |
I guess this will be fixed once #1200 is merged |
I know in classic this discussion had reasons to exist but with current PyScript, thisis the contract:
For the |
Checklist
What happened?
Hello, i already used the py-* functionality to call a python function like this:
Now this doesn't work anymore.
I noted that if i put brackets on function call like this form_field_changed() then the function is executed but comply that it require an argument (event)
is the Behaviour changed?
how can i access the event object?
Thanks
What browsers are you seeing the problem on? (if applicable)
Firefox, Chrome, Safari, Microsoft Edge, Other
Console info
No response
Additional Context
No response
The text was updated successfully, but these errors were encountered: