Guide Menu

Event Listeners

Binder makes it easy to attach event listeners to your elements using @{eventType}={action} syntax, eg. @click="someMethod" where click is the event to listen for and someMethod is a method on your controller class.

click counter

Clicks: {this.args.clicks}

View Source
<click-counter :clicks=0>
    <p :render>Clicks: {this.args.clicks}</p>
    <button @click.eval.render="this.args.clicks++">Click me!</button>
</click-counter>

<script type="module">
    import { registerControllers, Controller } from '/static/js/binder/binder.js';
    registerControllers(Controller.withTag("click-counter"));
</script>

Modifiers

There are a couple of modifiers that can be used when setting up event listeners. Modifiers can be mixed and appear in any order.

.prevent

The .prevent modifier will call event.preventDefault() and stop events from triggering their default actions.

.stop

The .stop modifier will call event.stopPropagation() and stop events from bubbling up the DOM.

.eval

The .eval modifier will evaluate the action instead of treating it as a method on the controller, allowing you to use any arbitrary JavaScript expression as your action.
The controller instance will be bound to this allowing you to still access the controller instance and the event object will be available as e.

<some-controller>
    <button @click.eval="alert(`Clicked ${e.target}!`)">Click me!</button>
</some-controller>

.render

The .render modifier will call this.render() on the controller after your event code is executed.
If your event handler is async then the render will happen after the promise resolves.


Emiting Events

Controllers can also emit custom events by calling this.emit(eventName, eventData), and listen for events using this.listenFor(target, "event-name", callback) allowing seamless communication between controllers.

events example

Total clicks: {this.data.clicks}

Last click was from: {this.data.lastClick}

View Source
<!-- 4 separate controllers -->
<total-clicks>
    <p :render>Total clicks: {this.data.clicks}</p>
    <p :render>Last click was from: {this.data.lastClick}</p>
</total-clicks>

<clicker-item id="clicker-a">
    <button @click="emitClick">A</button>
</clicker-item>

<clicker-item id="clicker-b">
    <button @click="emitClick">B</button>
</clicker-item>

<clicker-item id="clicker-c">
    <button @click="emitClick">C</button>
</clicker-item>

<script type="module">
    import { registerControllers, Controller } from '/static/js/binder/binder.js';

    class TotalClicks extends Controller {
        init() {
            this.data.clicks = 0;
            this.data.lastClick = "none";

            // We can either listen on the `window` for events which bubble up or listen on a specific element
            // If we listen on `this` we would only get events emitted from child elements
            // If we listen on `this.parentNode` we would get events from children and siblings
            this.listenFor(window, "clicker-clicked", e => {
                this.data.clicks++;
                this.data.lastClick = e.detail.from;
                this.render();
            });
        }
    }

    class ClickerItem extends Controller {
        emitClick() {
            // We can emit regular events like `click` or `change` or any arbitrary custom event
            // We can also pass data along with the event
            this.emit("clicker-clicked", {
                from: this.id
            });
        }
    }

    registerControllers(TotalClicks, ClickerItem);
</script>


Next: Binding