[DOC] improve the documentation

closes #340
closes #344
closes #352
closes #355
This commit is contained in:
Géry Debongnie
2019-10-20 09:01:38 +02:00
parent 1bb4577ec1
commit 29c2c5b9c9
7 changed files with 122 additions and 91 deletions
+15 -13
View File
@@ -97,7 +97,7 @@ state does nothing more:
```js
class Counter extends Component {
static template = xml`<div t-on-click="increment"><t t-esc="state.value"/></div>`;
state = {value: 0};
state = { value: 0 };
increment() {
this.state.value++;
@@ -131,7 +131,7 @@ const { useState } = owl.hooks;
class Counter extends Component {
static template = xml`<div t-on-click="increment"><t t-esc="state.value"/></div>`;
state = useState({value: 0});
state = useState({ value: 0 });
increment() {
this.state.value++;
@@ -140,6 +140,7 @@ class Counter extends Component {
```
Obviously, we can call the `useState` hook more than once:
```js
const { useState } = owl.hooks;
@@ -149,8 +150,8 @@ class Counter extends Component {
<span t-on-click="increment(counter1)"><t t-esc="counter1.value"/></span>
<span t-on-click="increment(counter2)"><t t-esc="counter2.value"/></span>
</div>`;
counter1 = useState({value: 0});
counter2 = useState({value: 0});
counter1 = useState({ value: 0 });
counter2 = useState({ value: 0 });
increment(counter) {
counter.value++;
@@ -405,8 +406,8 @@ component was patched. Note that this hook will not be called if the compoent is
not in the DOM (this can happen with components with `t-keepalive`).
Updating the component state in this hook is possible, but not encouraged.
One need to be careful, because updates here will cause rerender, which in
turn will cause other calls to patched. So, we need to be particularly
One needs to be careful, because updates here will create an additional rendering, which in
turn will cause other calls to the `patched` method. So, we need to be particularly
careful at avoiding endless cycles.
#### `willUnmount()`
@@ -429,7 +430,7 @@ This is the opposite method of `mounted`.
The `catchError` method is useful when we need to intercept and properly react
to (rendering) errors that occur in some sub components. See the section on
[error handling](#error-handling)
[error handling](#error-handling).
### Root Component
@@ -783,7 +784,7 @@ Like event handling, the `t-model` directive accepts some modifiers:
| Modifier | Description |
| --------- | -------------------------------------------------------------------- |
| `.lazy` | update the value on the `change` event (default is on `input` event) |
| `.number` | tries to parse the value to a number (using `parseFloat`) |
| `.number` | try to parse the value to a number (using `parseFloat`) |
| `.trim` | trim the resulting value |
For example:
@@ -799,9 +800,10 @@ Note: the online playground has an example to show how it works.
### `t-key` Directive
Even though Owl tries to be as declarative as possible, some DOM state is still
locked inside the DOM: for example, the scrolling state, the current user selection,
the focused element or the state of an input. This is why we use a virtual dom
Even though Owl tries to be as declarative as possible, the DOM does not fully
expose its state declaratively in the DOM tree. For example, the scrolling state,
the current user selection, the focused element or the state of an input are not
set as attribute in the DOM tree. This is why we use a virtual dom
algorithm to keep the actual DOM node as much as possible. However, this is
sometimes not enough, and we need to help Owl decide if an element is actually
the same, or is different. The `t-key` directive is used to give an identity to an element.
@@ -913,7 +915,7 @@ Here is what Owl will do:
As an application becomes complex, it may be quite unsafe to define props in an informal way. This leads to two issues:
- hard to tell how a component should be used, by looking at its code.
- unsafe, it is easy to send wrong props into a component, either by refactoring a component, or one of its parent.
- unsafe, it is easy to send wrong props into a component, either by refactoring a component, or one of its parents.
A props type system would solve both issues, by describing the types and shapes
of the props. Here is how it works in Owl:
@@ -1169,7 +1171,7 @@ from lifecycle hooks): the `catchError` hook.
Whenever the `catchError` lifecycle hook is implemented, all errors coming from
sub components rendering and/or lifecycle method calls will be caught and given
to the `catchError` method. This allow us to properly handle the error, and to
to the `catchError` method. This allows us to properly handle the error, and to
not break the application.
For example, here is how we could implement an `ErrorBoundary` component:
+1 -1
View File
@@ -11,7 +11,7 @@
## Overview
The `Context` object provides a way to share data between an arbitrary number
of component. Usually, data is passed from a parent to its children component,
of components. Usually, data is passed from a parent to its children component,
but when we have to deal with some mostly global information, this can be
annoying, since each component will need to pass the information to each children,
even though some or most of them will not use the information.
+3 -3
View File
@@ -374,7 +374,7 @@ not the solution to every problem.
class A extends Component {
constructor(...args) {
super(...args);
useMySpecificHook()
useMySpecificHook();
}
}
@@ -382,7 +382,7 @@ not the solution to every problem.
class B extends Component {
constructor(...args) {
super(...args);
this.performSpecificTask()
this.performSpecificTask();
}
}
```
@@ -402,7 +402,7 @@ not the solution to every problem.
}
```
As you can see, this does not *hook* into the internal of the component. It
As you can see, this does not _hook_ into the internal of the component. It
simply returns a global object, which is difficult to mock.
A better way would be to do something like this: get the reference from the
+38 -6
View File
@@ -1,10 +1,16 @@
# 🦉 Observer 🦉
Owl needs to be able to react to state changes. For example, whenever the state
of a component is changed, we need to rerender it. To help with that, we have
an Observer class. Its job is to observe some object state, and react to any
change. To do that, it recursively replaces all keys of the observed state by
getters and setters.
of a component is changed, Owl needs to rerender it. To help with that, there is
an Observer class. Its job is to observe the state of an object (or array), and
to react to any change. The observer is implemented with the native `Proxy`
object. Note that this means that it will not work on older browsers.
Note that the `Observer` is used by the `useState` and `useContext` hooks. This
is the way most Owl applications will create observers. For the majority of
use cases, there is no need to directly instantiate an observer.
## Example
For example, this code will display `update` in the console:
@@ -16,5 +22,31 @@ const obj = observer.observe({ a: { b: 1 } });
obj.a.b = 2;
```
The observer is implemented with the native `Proxy` object. Note that this
means that it will not work on older browsers.
This example shows that an observer can observe nested properties.
## Reference
**observe** An observer can observe multiple values with the `observe` method.
This method takes an object or an array as its argument and will return a proxy
(which is mapped to the initial object/array). With this proxy, the observer
can detect whenever any internal value is changed.
**Registering a callback** Whenever an observer sees a state change, it will
call its `notifyCB` method. No additional information is given to the callback.
**deepRevNumber** Each observed value has an internal revision number, which
is incremented every time the value is observed. Sometimes, it can be useful
to obtain that number:
```js
const observer = new owl.Observer();
const obj = observer.observe({ a: { b: 1 } });
observer.deepRevNumber(obj.a); // 1
obj.a.b = 2;
observer.deepRevNumber(obj.a); // 2
```
The `deepRevNumber` can also return 0, which indicates that the value is not
observed.
-1
View File
@@ -44,7 +44,6 @@ owl
whenReady
```
Note that for convenience, the `useState` hook is also exported at the root of the `owl` object.
## Reference
+5 -7
View File
@@ -38,14 +38,14 @@ const actions = {
state.todos.push({
id: state.nextId++,
message,
isCompleted: false,
isCompleted: false
});
}
};
const state = {
todos: [],
nextId: 1,
nextId: 1
};
const store = new owl.Store({ state, actions });
@@ -113,6 +113,7 @@ const actions = {
```
The first argument to an action method is an object with four keys:
- `state`: the current state of the store content,
- `dispatch`: a function that can be used to dispatch other actions,
- `getters`: an object containing all getters defined in the store,
@@ -200,8 +201,7 @@ const post = store.getters.getPost(id);
Getters take _at most_ one argument.
Note that getters are cached if they don't take any argument, or their argument
is a string or a number.
Note that getters are not cached.
### Connecting a Component
@@ -212,7 +212,6 @@ can be done with the help of the three store hooks:
- `useDispatch` to get a reference to a dispatch function,
- `useGetters` to get a reference to the getters defined in the store.
Assume we have this store:
```javascript
@@ -223,7 +222,7 @@ const actions = {
};
const state = {
counter: {value: 0}
counter: { value: 0 }
};
const store = new owl.Store({ state, actions });
```
@@ -256,7 +255,6 @@ two arguments:
- optionally, an object with a `store` key (if we want to override the default
store) and an equality function (if we want to specialize the comparison).
If the `useStore` callback selects a sub part of the store state, the component
will only be rerendered whenever this part of the state changes. Otherwise, it
will perform a strict equality check and will update the component every time this
+10 -10
View File
@@ -12,7 +12,6 @@ functions are all available in the `owl.utils` namespace.
- [`debounce`](#debounce): limiting rate of function calls
- [`shallowEqual`](#shallowequal): shallow object comparison
## `whenReady`
The function `whenReady` returns a `Promise` resolved when the DOM is ready (if
@@ -20,7 +19,7 @@ not ready yet, resolved directly otherwise). If called with a callback as
argument, it executes it as soon as the DOM ready (or directly).
```js
Promise.all([loadFile('templates.xml'), owl.utils.whenReady()]).then(function([templates]) {
Promise.all([loadFile("templates.xml"), owl.utils.whenReady()]).then(function([templates]) {
const qweb = new owl.QWeb(templates);
const app = new App({ qweb });
app.mount(document.body);
@@ -45,6 +44,7 @@ properly reacts when it is ready. Also, it is smart: it maintains a list of urls
previously loaded (or currently being loaded), and prevent doing twice the work.
For example, it is useful for lazy loading external libraries:
```js
class MyComponent extends owl.Component {
willStart() {
@@ -93,6 +93,7 @@ class BadComponent extends Component {
}
}
```
In this case, the content of the `div` will be parsed as html, which may inject
unwanted behaviour. To fix this, the `escape` function will simply transform a
string into an escaped version of the same string, which will be properly displayed
@@ -101,10 +102,9 @@ escaped to the string: `"&lt;ok&gt;"`). So, the bad example above can be fixed
with the following change:
```js
this.divRef.el.innerHTML = owl.utils.escape(this.state.value);
this.divRef.el.innerHTML = owl.utils.escape(this.state.value);
```
## `debounce`
The `debounce` function is useful when we want to limit the number of times some
@@ -112,6 +112,7 @@ function/action is perfomed. For example, this may be useful to prevent issue
with people double clicking on a button.
It takes three arguments:
- `func` (function): this is the function that will be rate limited
- `wait` (number): this is the number of milliseconds that we want to use to
rate limit the function `func`
@@ -122,21 +123,20 @@ It takes three arguments:
It returns a function. For example:
```js
const debounce = owl.utils.debounce
window.addEventListener('mousemove', debounce(doSomething, 100));
const debounce = owl.utils.debounce;
window.addEventListener("mousemove", debounce(doSomething, 100));
```
As this example shows, it is usualy useful for event handlers which are triggered
very quickly, such as `scroll` or `mousemove` events.
## `shallowEqual`
This function checks if two objects have the same values assigned to each keys:
```js
shallowEqual({a:1, b: 2}, {a:1, b:2}); // true
shallowEqual({a:1, b: 2}, {a:1, b:3}); // false
shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2 }); // true
shallowEqual({ a: 1, b: 2 }, { a: 1, b: 3 }); // false
```
However, for performance reasons, it assumes that the two objects have the same
@@ -144,5 +144,5 @@ keys. If we are in a situation where this is not guaranteed, the following code
will work:
```js
const completeShallowEqual = (a,b) => shallowEqual(a,b) && shallowEqual(b,a);
const completeShallowEqual = (a, b) => shallowEqual(a, b) && shallowEqual(b, a);
```