[DOC] component: add a section on semantics

closes #89
This commit is contained in:
Géry Debongnie
2019-05-08 11:59:40 +02:00
parent f7f8c34e1c
commit 2f70a7a352
+105 -20
View File
@@ -9,6 +9,7 @@
- [Properties](#properties) - [Properties](#properties)
- [Methods](#methods) - [Methods](#methods)
- [Lifecycle](#lifecycle) - [Lifecycle](#lifecycle)
- [Semantics](#semantics)
## Overview ## Overview
@@ -67,9 +68,9 @@ change to it will cause a rerendering.
## Composition ## Composition
The example above shows a QWeb template with a `t-on-click` directive. Widget The example above shows a QWeb template with a `t-on-click` directive. Widget
templates are standard [QWeb](qweb.md) templates, but with an extra directive: templates are standard [QWeb](qweb.md) templates, but with an extra directive:
`t-widget`. With the `t-widget` directive, widget templates can declare sub `t-widget`. With the `t-widget` directive, widget templates can declare sub
widgets: widgets:
```xml ```xml
@@ -91,7 +92,7 @@ In this example, the `ParentWidget`'s template creates a widget `MyWidget` just
after the span. See the [QWeb](qweb.md) documentation for more information on the after the span. See the [QWeb](qweb.md) documentation for more information on the
`t-widget` directive. `t-widget` directive.
Note that the rendering context for the template is the widget itself. This means Note that the rendering context for the template is the widget itself. This means
that the template can access `state`, `props`, `env`, or any methods defined in the widget. that the template can access `state`, `props`, `env`, or any methods defined in the widget.
**CSS and style:** there is some specific support to allow the parent to declare **CSS and style:** there is some specific support to allow the parent to declare
@@ -106,17 +107,15 @@ root widget element.
Warning: there is a small caveat with dynamic class attributes: since Owl needs Warning: there is a small caveat with dynamic class attributes: since Owl needs
to be able to add/remove proper classes whenever necessary, it needs to be aware to be able to add/remove proper classes whenever necessary, it needs to be aware
of the possible classes. Otherwise, it will not be able to make the difference of the possible classes. Otherwise, it will not be able to make the difference
between a valid css class added by the component, or some custom code, and a between a valid css class added by the component, or some custom code, and a
class that need to be removed. This is why we only support the explicit syntax class that need to be removed. This is why we only support the explicit syntax
with a class object: with a class object:
```js ```js
<t t-widget="MyWidget" t-att-class="{a: state.flagA, b: state.flagB}"/> <t t-widget="MyWidget" t-att-class="{a: state.flagA, b: state.flagB}" />
``` ```
## Reference ## Reference
An Owl component is a small class which represent a widget or some UI element. An Owl component is a small class which represent a widget or some UI element.
@@ -196,15 +195,15 @@ A solid and robust component system needs useful hooks/methods to help
developers write components. Here is a complete description of the lifecycle of developers write components. Here is a complete description of the lifecycle of
a owl component: a owl component:
| Method | Description | | Method | Description |
| ------------------------------------------------ | --------------------------------------- | | ------------------------------------------------ | ----------------------------------------------------- |
| **[constructor](#constructor)** | constructor | | **[constructor](#constructor)** | constructor |
| **[willStart](#willStart)** | async, before first rendering | | **[willStart](#willStart)** | async, before first rendering |
| **[mounted](#mounted)** | just after component is rendered and added to the DOM | | **[mounted](#mounted)** | just after component is rendered and added to the DOM |
| **[willUpdateProps](#willupdatepropsnextprops)** | async, before props update | | **[willUpdateProps](#willupdatepropsnextprops)** | async, before props update |
| **[willPatch](#willpatch)** | just before the DOM is patched | | **[willPatch](#willpatch)** | just before the DOM is patched |
| **[patched](#patchedsnapshot)** | just after the DOM is patched | | **[patched](#patchedsnapshot)** | just after the DOM is patched |
| **[willUnmount](#willUnmount)** | just before removing component from DOM | | **[willUnmount](#willUnmount)** | just before removing component from DOM |
Notes: Notes:
@@ -212,7 +211,7 @@ Notes:
then on children, and `[Xed]` are called in the reverse order: first children, then on children, and `[Xed]` are called in the reverse order: first children,
then parent. then parent.
- no hook method should ever be called manually. They are supposed to be - no hook method should ever be called manually. They are supposed to be
called by the owl framework whenever it is required. called by the owl framework whenever it is required.
#### `constructor(parent, props)` #### `constructor(parent, props)`
@@ -307,7 +306,7 @@ scrollbar.
Note that modifying the state object is not allowed here. This method is called just Note that modifying the state object is not allowed here. This method is called just
before an actual DOM patch, and is only intended to be used to save some local before an actual DOM patch, and is only intended to be used to save some local
DOM state. Also, it will not be called if the widget is not in the DOM (this can DOM state. Also, it will not be called if the widget is not in the DOM (this can
happen with widgets with `t-keepalive`). happen with widgets with `t-keepalive`).
The return value of this method will be given as the first argument of the The return value of this method will be given as the first argument of the
@@ -320,7 +319,7 @@ likely via a change in its state/props or environment).
This method is not called on the initial render. It is useful to interact This method is not called on the initial render. It is useful to interact
with the DOM (for example, through an external library) whenever the with the DOM (for example, through an external library) whenever the
component was patched. Note that this hook will not be called if the widget is component was patched. Note that this hook will not be called if the widget is
not in the DOM (this can happen with widgets with `t-keepalive`). not in the DOM (this can happen with widgets with `t-keepalive`).
The `snapshot` parameter is the result of the previous `willPatch` call. The `snapshot` parameter is the result of the previous `willPatch` call.
@@ -345,3 +344,89 @@ the DOM. This is a good place to remove some listeners, for example.
``` ```
This is the opposite method of `mounted`. This is the opposite method of `mounted`.
### Semantics
We give here an informal description of the way components are created/updated
in an application. Here, ordered lists describe actions that are executed
sequentially, bullet lists describe actions that are executed in parallel.
**Scenario 1: Initial Mounting** Imagine we want to render the following component tree:
```
A
/ \
B C
/ \
D E
```
Here is what happen whenever we mount the root
component (with some code like `app.mount(document.body)`).
1. `willStart` is called on `A`
2. when it is done, template `A` is rendered.
- widget `B` is created
1. `willStart` is called on `B`
2. template `B` is rendered
- widget `C` is created
1. `willStart` is called on `C`
2. template `C` is rendered
- widget `D` is created
1. `willStart` is called on `D`
2. template `D` is rendered
- widget `E` is created
1. `willStart` is called on `E`
2. template `E` is rendered
3. widget `A` is patched into a detached DOM element. This will create the actual
widget `A` DOM structure. The patching process will cause recursively the
patching of the `B`, `C`, `D` and `E` DOM trees. (so the actual full DOM tree is created
in one pass)
4. the widget `A` root element is actually appended to `document.body`
5. The method `mounted` is called recursively on all widgets in the following
order: `B`, `D`, `E`, `C`, `A`.
**Scenario 2: state change, rerendering**. Now, let's assume that the user clicked on some
button in `C`, and this results in a state update, which is supposed to:
- update `D`,
- remove `E`,
- add new widget `F`.
So, the component tree should look like this:
```
A
/ \
B C
/ \
D F
```
Here is what Owl will do:
1. because of a state change, the method `render` is called on `C`
2. template `C` is rendered again
- widget `D` is updated:
1. hook `willUpdateProps` is called on `D` (async)
2. template `D` is rerendered
- widget `F` is created:
1. hook `willStart` is called on `E` (async)
2. template `F` is rerendered
3. `willPatch` hooks are called recursively on widgets `C`, `D` (not on `F`,
because it is not mounted yet)
4. widget `C` is patched, which will cause recursively:
1. patching of `D`,
2. `willUnmount` hook on `E`, then destruction of `E`,
3. (initial) patching of `F`, then hook `mounted` is called on `F`
5. `patched` hooks are called on `D`, `C`