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.
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.