dash-extensions icon indicating copy to clipboard operation
dash-extensions copied to clipboard

EventListener nested properties not working

Open FRosner opened this issue 2 years ago • 4 comments

Problem

I am trying to use an EventListener to handle clicks on some HTML buttons which are not dash components (because they are inside a data table and data tables don't support dash components 😢 ).

In order to know what dash code to call based on the button clicked, I want to make use of data attributes which contain information about the relevant python code to call and what data to pass (e.g. which row the button was in).

However, it seems that the property extraction code does not give me the full values, but only a subset. The logging debug output shows:

image

While in the callback I only get:

{'srcElement.attributes': {'0': {}, '1': {}, '2': {}, '3': {}, '4': {}}}

Here's the event with the relevant properties I'm interesting in:

CLICK_EVENT = {"event": "click", "props": ["srcElement.attributes"]}

Here's my relevant layout:

EventListener(events=[CLICK_EVENT], logging=True, id=EVENT_LISTENER_ID),
dcc.Store(id=EVENT_LISTENER_STORE_ID, data={}),

Here's the callback:

@dashapp.callback(
    Output(EVENT_LISTENER_STORE_ID, "data"),
    Input(EVENT_LISTENER_ID, "n_events"),
    State(EVENT_LISTENER_ID, "event")
)
def click_event(n_events, event):
    if event is None:
        raise PreventUpdate
    print(event)
    return ""

Alternatively, I tried requesting "srcElement.attributes.data-click.value" directly in the event, which works. Is there a way to get nested props?

FRosner avatar Aug 03 '23 06:08 FRosner

It looks like all values are captures (you get 5 values, and the list has length 5), but the content is empty. My first guess would be that the content is not JSON serializeable - could that be the case? Otherwise, could you post a MWE (i.e. a small piece of code that runs and demonstrates the issue), then I can do a bit more debugging.

emilhe avatar Aug 03 '23 07:08 emilhe

My first guess would be that the content is not JSON serializeable - could that be the case?

It is a NamedNodeMap, maybe this is the issue indeed. https://stackoverflow.com/questions/25497475/html-attributes-to-json

Otherwise, could you post a MWE (i.e. a small piece of code that runs and demonstrates the issue), then I can do a bit more debugging.

I'm new to the ecosystem, is there some online dash playground I can use?

FRosner avatar Aug 03 '23 07:08 FRosner

Alternatively, you can use the example from https://www.dash-extensions.com/components/event_listener

from dash_extensions.enrich import DashProxy, html, Input, Output, State
from dash_extensions import EventListener
from dash.exceptions import PreventUpdate

# JavaScript event(s) that we want to listen to and what properties to collect.
event = {"event": "click", "props": ["srcElement.attributes"]}
# Create small example app
app = DashProxy()
app.layout = html.Div([
    EventListener(
        html.Div("Click here!", className="stuff"),
        events=[event], logging=True, id="el"
    ),
    html.Div(id="log")
])

@app.callback(Output("log", "children"), Input("el", "n_events"), State("el", "event"))
def click_event(n_events, e):
    if e is None:
        raise PreventUpdate()
    return ",".join(f"{prop} is '{e[prop]}' " for prop in event["props"]) + f" (number of clicks is {n_events})"

if __name__ == "__main__":
    app.run_server()

FRosner avatar Aug 03 '23 07:08 FRosner

Yes, I believe that is indeed the issue. With the current implementation, you must "drill down" until you reach properties which are (trivially) JSON serializable, which is indeed the case for srcElement.attributes.data-click.value.

emilhe avatar Aug 03 '23 08:08 emilhe