[DOC] add dedicated page on event handling

part of #632
This commit is contained in:
Géry Debongnie
2020-02-15 10:18:12 +01:00
parent 69d35abe4f
commit 047a9c8993
7 changed files with 141 additions and 127 deletions
+1 -1
View File
@@ -124,7 +124,7 @@ class Parent extends Component {
In this example, the `OrderLine` component trigger a `add-to-order` event. This In this example, the `OrderLine` component trigger a `add-to-order` event. This
will generate a DOM event which will bubble along the DOM tree. It will then be will generate a DOM event which will bubble along the DOM tree. It will then be
intercepted by the parent component, which will then get the line (from the intercepted by the parent component, which will then get the line (from the
`detail` key) and then increment its quantity. See the section on [event handling](../reference/component.md#event-handling) `detail` key) and then increment its quantity. See the page on [event handling](../reference/event_handling.md)
for more details on how events work. for more details on how events work.
Note that this example would have also worked if the `OrderLine` component Note that this example would have also worked if the `OrderLine` component
+1 -1
View File
@@ -446,7 +446,7 @@ Now, this is an interesting situation: the task is displayed by the `Task`
component, but it is not the owner of its state, so it cannot modify it. Instead, component, but it is not the owner of its state, so it cannot modify it. Instead,
we want to communicate the request to toggle a task to the `App` component. we want to communicate the request to toggle a task to the `App` component.
Since `App` is a parent of `Task`, we can Since `App` is a parent of `Task`, we can
[trigger](../reference/component.md#event-handling) an event in `Task` and listen [trigger](../reference/event_handling.md) an event in `Task` and listen
for it in `App`. for it in `App`.
In `Task`, change the `input` to: In `Task`, change the `input` to:
+1
View File
@@ -24,6 +24,7 @@ provided by Owl.
- [Context](reference/context.md) - [Context](reference/context.md)
- [Environment](reference/environment.md) - [Environment](reference/environment.md)
- [Event Bus](reference/event_bus.md) - [Event Bus](reference/event_bus.md)
- [Event Handling](reference/event_handling.md)
- [Hooks](reference/hooks.md) - [Hooks](reference/hooks.md)
- [Miscellaneous Components](reference/misc.md) - [Miscellaneous Components](reference/misc.md)
- [Observer](reference/observer.md) - [Observer](reference/observer.md)
-123
View File
@@ -12,7 +12,6 @@
- [Lifecycle](#lifecycle) - [Lifecycle](#lifecycle)
- [Root Component](#root-component) - [Root Component](#root-component)
- [Composition](#composition) - [Composition](#composition)
- [Event Handling](#event-handling)
- [Form Input Bindings](#form-input-bindings) - [Form Input Bindings](#form-input-bindings)
- [References](#references) - [References](#references)
- [Dynamic sub components](#dynamic-sub-components) - [Dynamic sub components](#dynamic-sub-components)
@@ -551,128 +550,6 @@ with a class object:
<MyComponent t-att-class="{a: state.flagA, b: state.flagB}" /> <MyComponent t-att-class="{a: state.flagA, b: state.flagB}" />
``` ```
### Event Handling
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.
1. Register an event handler on a DOM node (_pure_ DOM event)
2. Register an event handler on a component (_pure_ DOM event)
3. Register an event handler on a DOM node (_business_ DOM event)
4. 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`).
```xml
<button t-on-click="someMethod">Do something</button>
```
This will be roughly translated in javascript like this:
```js
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.
```xml
<MyComponent t-on-menu-loaded="someMethod" />
```
```js
class MyComponent {
someWhere() {
const payload = ...;
this.trigger('menu-loaded', payload);
}
}
```
The call to `trigger` generates an `OwlEvent`, a subclass of [_CustomEvent_](https://developer.mozilla.org/docs/Web/Guide/Events/Creating_and_triggering_events)
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.
```js
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,
```xml
<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,
```xml
<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:
```js
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.
```xml
<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 |
```xml
<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,
```xml
<button t-on-click.stop="">Do something</button>
```
This will simply stop the propagation of the event.
### Form Input Bindings ### Form Input Bindings
It is very common to need to be able to read the value out of an html `input` (or It is very common to need to be able to read the value out of an html `input` (or
+136
View File
@@ -0,0 +1,136 @@
# 🦉 Event Handling 🦉
## Content
- [Event Handling](#event-handling)
- [Business DOM Events](#business-dom-events)
- [Inline Event Handlers](#inline-event-handlers)
- [Modifiers](#modifiers)
## Event Handling
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.
1. Register an event handler on a DOM node (_pure_ DOM event)
2. Register an event handler on a component (_pure_ DOM event)
3. Register an event handler on a DOM node (_business_ DOM event)
4. 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`).
```xml
<button t-on-click="someMethod">Do something</button>
```
This will be roughly translated in javascript like this:
```js
button.addEventListener("click", component.someMethod.bind(component));
```
The suffix (`click` in this example) is simply the name of the actual DOM
event.
## Business DOM Events
A _business_ DOM event is triggered by a call to `trigger` on a component.
```xml
<MyComponent t-on-menu-loaded="someMethod" />
```
```js
class MyComponent {
someWhere() {
const payload = ...;
this.trigger('menu-loaded', payload);
}
}
```
The call to `trigger` generates an `OwlEvent`, a subclass of [_CustomEvent_](https://developer.mozilla.org/docs/Web/Guide/Events/Creating_and_triggering_events)
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.
```js
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,
```xml
<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.
## Inline Event Handlers
One can also directly specify inline statements. For example,
```xml
<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:
```js
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.
```xml
<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>
```
## Modifiers
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 |
```xml
<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,
```xml
<button t-on-click.stop="">Do something</button>
```
This will simply stop the propagation of the event.
+1 -1
View File
@@ -95,7 +95,7 @@ The teleported piece is updated as any other `Component`'s DOM and in the same s
Namely the teleported piece will be updated in function of its parents components, and patched as Namely the teleported piece will be updated in function of its parents components, and patched as
a normal child. a normal child.
The [_business_ events](component.md#event-handling) triggered by a child component will be stopped The [_business_ events](event_handling.md#business-dom-events) triggered by a child component will be stopped
to not bubble outside of the `target`. They will, on the other hand, be re-directed onto the to not bubble outside of the `target`. They will, on the other hand, be re-directed onto the
`Portal`'s root node and bubble up the DOM as if it were triggered by a regular child component. `Portal`'s root node and bubble up the DOM as if it were triggered by a regular child component.
+1 -1
View File
@@ -71,7 +71,7 @@ needs. Here is a list of all Owl specific directives:
| `t-component`, `t-props` | [Defining a sub component](component.md#composition) | | `t-component`, `t-props` | [Defining a sub component](component.md#composition) |
| `t-ref` | [Setting a reference to a dom node or a sub component](component.md#references) | | `t-ref` | [Setting a reference to a dom node or a sub component](component.md#references) |
| `t-key` | [Defining a key (to help virtual dom reconciliation)](#loops) | | `t-key` | [Defining a key (to help virtual dom reconciliation)](#loops) |
| `t-on-*` | [Event handling](component.md#event-handling) | | `t-on-*` | [Event handling](event_handling.md) |
| `t-transition` | [Defining an animation](animations.md#css-transitions) | | `t-transition` | [Defining an animation](animations.md#css-transitions) |
| `t-slot` | [Rendering a slot](slots.md) | | `t-slot` | [Rendering a slot](slots.md) |
| `t-model` | [Form input bindings](component.md#form-input-bindings) | | `t-model` | [Form input bindings](component.md#form-input-bindings) |