In a component's template, it is useful to be able to register handlers on DOM elements to some specific events. This is what makes a template alive. There are four different use cases.
- Register an event handler on a DOM node (pure DOM event)
- Register an event handler on a component (pure DOM event)
- Register an event handler on a DOM node (business DOM event)
- Register an event handler on a component (business DOM event)
A pure DOM event is directly triggered by a user interaction (e.g. a click
).
<button t-on-click="someMethod">Do something</button>
This will be roughly translated in javascript like this:
button.addEventListener("click", component.someMethod.bind(component));
The suffix (click
in this example) is simply the name of the actual DOM
event.
A business DOM event is triggered by a call to trigger
on a component.
<MyComponent t-on-menu-loaded="someMethod" />
class MyComponent {
someWhere() {
const payload = ...;
this.trigger('menu-loaded', payload);
}
}
The call to trigger
generates an OwlEvent
, a subclass of CustomEvent
with an additional attribute originalComponent
(the component that triggered
the event). The generated event is of type menu-loaded
and dispatches it on
the component's DOM element (this.el
). The event bubbles and is cancelable.
The parent component listening to event menu-loaded
will receive the payload
in its someMethod
handler (in the detail
property of the event), whenever
the event is triggered.
class ParentComponent {
someMethod(ev) {
const payload = ev.detail;
...
}
}
By convention, we use KebabCase for the name of business events.
The t-on
directive allows to prebind its arguments. For example,
<button t-on-click="someMethod(expr)">Do something</button>
Here, expr
is a valid Owl expression, so it could be true
or some variable
from the rendering context.
Note that if you work with Typescript, the trigger
method is generic on the type of the payload.
You can then describe the type of the event, so you will see typing errors...
this.trigger<MyCustomPayload>("my-custom-event", payload);
myCustomEventHandler(ev: OwlEvent<MyCustomPayload>) { ... }
One can also directly specify inline statements. For example,
<button t-on-click="state.counter++">Increment counter</button>
Here, state
must be defined in the rendering context (typically the component)
as it will be translated to:
button.addEventListener("click", () => {
context.state.counter++;
});
Warning: inline expressions are evaluated in the context of the template. This means that they can access the component methods and properties. But if they set a key, the inline statement will actually not modify the component, but a key in a sub scope.
<button t-on-click="value = 1">Set value to 1 (does not work!!!)</button>
<button t-on-click="state.value = 1">Set state.value to 1 (work as expected)</button>
In order to remove the DOM event details from the event handlers (like calls to
event.preventDefault
) and let them focus on data logic, modifiers can be
specified as additional suffixes of the t-on
directive.
Modifier | Description |
---|---|
.stop |
calls event.stopPropagation() before calling the method |
.prevent |
calls event.preventDefault() before calling the method |
.self |
calls the method only if the event.target is the element itself |
.capture |
bind the event handler in capture mode. |
<button t-on-click.stop="someMethod">Do something</button>
Note that modifiers can be combined (ex: t-on-click.stop.prevent
), and that
the order may matter. For instance t-on-click.prevent.self
will prevent all
clicks while t-on-click.self.prevent
will only prevent clicks on the element
itself.
Finally, empty handlers are tolerated as they could be defined only to apply modifiers. For example,
<button t-on-click.stop="">Do something</button>
This will simply stop the propagation of the event.