emilhe / dash-extensions

The dash-extensions package is a collection of utility functions, syntax extensions, and Dash components that aim to improve the Dash development experience
https://www.dash-extensions.com/
MIT License
409 stars 57 forks source link

`EventListener` only triggers once unless `props` change #300

Closed TimChild closed 7 months ago

TimChild commented 7 months ago

Hi @emilhe, first of all, thanks for the excellent package you have created! Lots of great work in here!!

It's possible this behaviour is actually correct/intentional, but it was not obvious to me at first, and maybe would benefit from a note or warning in the docs.

When listening for the change event, I noticed that even though the console logs the change even (using logging=True in your EventListener), the callback associated with it only fires once unless the props value also changes.

Here's a minimal example that demonstrates the issue.


app.layout = html.Div(
    [
        html.Div("With prop that changes"),
        EventListener(
            id="event-listener-with-prop",
            events=[
                {
                    "event": "change",
                    "props": ["srcElement.value"],
                }
            ],
            logging=True,
            children=[
                dcc.Input(id="input-with-prop", type="text"),
            ],
        ),
        html.Div(id="output-with-prop"),
        html.Hr(),
        html.Div("Without prop that changes"),
        EventListener(
            id="event-listener-no-prop",
            events=[
                {
                    "event": "change",
                    "props": [],
                }
            ],
            logging=True,
            children=[
                dcc.Input(id="input-no-prop", type="text"),
            ],
        ),
        html.Div(id="output-no-prop"),
    ]
)

with_prop_counts = 0

@app.callback(
    Output("output-with-prop", "children"), Input("event-listener-with-prop", "event"), prevent_initial_call=True
)
def update_output(event):
    global with_prop_counts
    with_prop_counts += 1
    return f"Num times triggered: {with_prop_counts}\nEvent triggered with: {event}"

without_prop_counts = 0

@app.callback(Output("output-no-prop", "children"), Input("event-listener-no-prop", "event"), prevent_initial_call=True)
def update_output(event):
    global without_prop_counts
    without_prop_counts += 1
    return f"Num times triggered: {without_prop_counts}\nEvent triggered with: {event}"

if __name__ == "__main__":
    app.run(debug=True)

Both trigger once when typing text then changing focus away from the text box. image

But, if I change the text in both boxes and then change focus away again, only the one with a prop that changes updates. image

Possible that this relates to #282 as I expect that would have similar behaviour since there are no props specified.

emilhe commented 7 months ago

In Dash, callbacks are triggered on property changes, which is why e.g. button clicks are typically signaled by incrementing a counter. Hence, what you are seeing is indeed expected behavior.

TimChild commented 7 months ago

Thanks for the speedy response! Ok, well that does make sense, although it still might be worth a little note in the docs.

I was wrongly assuming that the EventListener was directly triggered by the js events and that the event prop was just a convenient way to get the event details... But, it does of course make more sense that the event propery is just like any other dash property... Anyway, it wasn't immediately obvious that I would need to add in a prop that I don't necessarily care about to catch the event I was listening for, and I imagine it might catch others out when using it for things like resize events as in #282.

Anyway, thanks again!

emilhe commented 7 months ago

Thanks! I'll keep that in mind 👍 . I don't think that it's related to #282 though.

emilhe commented 5 months ago

Docs are now updated. http://localhost:7879/components/event_listener

TimChild commented 5 months ago

@emilhe New docs look great! I think you meant to post this link :) https://www.dash-extensions.com/components/event_listener

emilhe commented 5 months ago

Ah, yes, you are correct 😀