mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[DOC] improve the documentation
closes #340 closes #344 closes #352 closes #355
This commit is contained in:
+32
-30
@@ -91,22 +91,22 @@ find a template with the component name (or one of its ancestors).
|
|||||||
|
|
||||||
### Reactive system
|
### Reactive system
|
||||||
|
|
||||||
OWL components are normal javascript classes. So, changing a component internal
|
OWL components are normal javascript classes. So, changing a component internal
|
||||||
state does nothing more:
|
state does nothing more:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
class Counter extends Component {
|
class Counter extends Component {
|
||||||
static template = xml`<div t-on-click="increment"><t t-esc="state.value"/></div>`;
|
static template = xml`<div t-on-click="increment"><t t-esc="state.value"/></div>`;
|
||||||
state = {value: 0};
|
state = { value: 0 };
|
||||||
|
|
||||||
increment() {
|
increment() {
|
||||||
this.state.value++;
|
this.state.value++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Clicking on the `Counter` component defined above will call the `increment`
|
Clicking on the `Counter` component defined above will call the `increment`
|
||||||
method, but it will not rerender the component. To fix that, one could add an
|
method, but it will not rerender the component. To fix that, one could add an
|
||||||
explicit call to `render` in `increment`:
|
explicit call to `render` in `increment`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -122,7 +122,7 @@ method.
|
|||||||
|
|
||||||
A better way is to use the reactive system: by using the `useState` hook (see the
|
A better way is to use the reactive system: by using the `useState` hook (see the
|
||||||
[hooks](hooks.md) section for more details), one can make Owl react to state
|
[hooks](hooks.md) section for more details), one can make Owl react to state
|
||||||
changes. The `useState` hook generates a proxy version of an object
|
changes. The `useState` hook generates a proxy version of an object
|
||||||
(this is done by an [observer](observer.md)), which allows the component to
|
(this is done by an [observer](observer.md)), which allows the component to
|
||||||
react to any change. So, the `Counter` example above can be improved like this:
|
react to any change. So, the `Counter` example above can be improved like this:
|
||||||
|
|
||||||
@@ -130,31 +130,32 @@ react to any change. So, the `Counter` example above can be improved like this:
|
|||||||
const { useState } = owl.hooks;
|
const { useState } = owl.hooks;
|
||||||
|
|
||||||
class Counter extends Component {
|
class Counter extends Component {
|
||||||
static template = xml`<div t-on-click="increment"><t t-esc="state.value"/></div>`;
|
static template = xml`<div t-on-click="increment"><t t-esc="state.value"/></div>`;
|
||||||
state = useState({value: 0});
|
state = useState({ value: 0 });
|
||||||
|
|
||||||
increment() {
|
increment() {
|
||||||
this.state.value++;
|
this.state.value++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Obviously, we can call the `useState` hook more than once:
|
Obviously, we can call the `useState` hook more than once:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { useState } = owl.hooks;
|
const { useState } = owl.hooks;
|
||||||
|
|
||||||
class Counter extends Component {
|
class Counter extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
<div>
|
<div>
|
||||||
<span t-on-click="increment(counter1)"><t t-esc="counter1.value"/></span>
|
<span t-on-click="increment(counter1)"><t t-esc="counter1.value"/></span>
|
||||||
<span t-on-click="increment(counter2)"><t t-esc="counter2.value"/></span>
|
<span t-on-click="increment(counter2)"><t t-esc="counter2.value"/></span>
|
||||||
</div>`;
|
</div>`;
|
||||||
counter1 = useState({value: 0});
|
counter1 = useState({ value: 0 });
|
||||||
counter2 = useState({value: 0});
|
counter2 = useState({ value: 0 });
|
||||||
|
|
||||||
increment(counter) {
|
increment(counter) {
|
||||||
counter.value++;
|
counter.value++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -169,7 +170,7 @@ to be called in the constructor.
|
|||||||
- **`env`** (Object): the component environment, which contains a QWeb instance.
|
- **`env`** (Object): the component environment, which contains a QWeb instance.
|
||||||
|
|
||||||
- **`props`** (Object): this is an object containing all the properties given by
|
- **`props`** (Object): this is an object containing all the properties given by
|
||||||
the parent to a child component. For example, in the following situation,
|
the parent to a child component. For example, in the following situation,
|
||||||
the parent component gives a `user` and a `color` value to the `ChildComponent`.
|
the parent component gives a `user` and a `color` value to the `ChildComponent`.
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
@@ -182,7 +183,7 @@ to be called in the constructor.
|
|||||||
As such, it should not ever be modified by the component (otherwise you risk
|
As such, it should not ever be modified by the component (otherwise you risk
|
||||||
unintended effects, since the parent may not be aware of the change)!!
|
unintended effects, since the parent may not be aware of the change)!!
|
||||||
|
|
||||||
The `props` can be modified dynamically by the parent. In that case, the
|
The `props` can be modified dynamically by the parent. In that case, the
|
||||||
component will go through the following lifecycle methods: `willUpdateProps`,
|
component will go through the following lifecycle methods: `willUpdateProps`,
|
||||||
`willPatch` and `patched`.
|
`willPatch` and `patched`.
|
||||||
|
|
||||||
@@ -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`).
|
not in the DOM (this can happen with components with `t-keepalive`).
|
||||||
|
|
||||||
Updating the component state in this hook is possible, but not encouraged.
|
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
|
One needs to be careful, because updates here will create an additional rendering, which in
|
||||||
turn will cause other calls to patched. So, we need to be particularly
|
turn will cause other calls to the `patched` method. So, we need to be particularly
|
||||||
careful at avoiding endless cycles.
|
careful at avoiding endless cycles.
|
||||||
|
|
||||||
#### `willUnmount()`
|
#### `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
|
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
|
to (rendering) errors that occur in some sub components. See the section on
|
||||||
[error handling](#error-handling)
|
[error handling](#error-handling).
|
||||||
|
|
||||||
### Root Component
|
### Root Component
|
||||||
|
|
||||||
@@ -783,7 +784,7 @@ Like event handling, the `t-model` directive accepts some modifiers:
|
|||||||
| Modifier | Description |
|
| Modifier | Description |
|
||||||
| --------- | -------------------------------------------------------------------- |
|
| --------- | -------------------------------------------------------------------- |
|
||||||
| `.lazy` | update the value on the `change` event (default is on `input` event) |
|
| `.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 |
|
| `.trim` | trim the resulting value |
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@@ -799,9 +800,10 @@ Note: the online playground has an example to show how it works.
|
|||||||
|
|
||||||
### `t-key` Directive
|
### `t-key` Directive
|
||||||
|
|
||||||
Even though Owl tries to be as declarative as possible, some DOM state is still
|
Even though Owl tries to be as declarative as possible, the DOM does not fully
|
||||||
locked inside the DOM: for example, the scrolling state, the current user selection,
|
expose its state declaratively in the DOM tree. For example, the scrolling state,
|
||||||
the focused element or the state of an input. This is why we use a virtual dom
|
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
|
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
|
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.
|
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:
|
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.
|
- 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
|
A props type system would solve both issues, by describing the types and shapes
|
||||||
of the props. Here is how it works in Owl:
|
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
|
Whenever the `catchError` lifecycle hook is implemented, all errors coming from
|
||||||
sub components rendering and/or lifecycle method calls will be caught and given
|
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.
|
not break the application.
|
||||||
|
|
||||||
For example, here is how we could implement an `ErrorBoundary` component:
|
For example, here is how we could implement an `ErrorBoundary` component:
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The `Context` object provides a way to share data between an arbitrary number
|
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
|
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,
|
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.
|
even though some or most of them will not use the information.
|
||||||
|
|||||||
+24
-24
@@ -220,18 +220,18 @@ be ready whenever the component is rendered:
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
function useLoader() {
|
function useLoader() {
|
||||||
const component = Component.current;
|
const component = Component.current;
|
||||||
const record = useState({});
|
const record = useState({});
|
||||||
onWillStart(async () => {
|
onWillStart(async () => {
|
||||||
const recordId = component.props.id;
|
const recordId = component.props.id;
|
||||||
Object.assign(record, await fetchSomeRecord(recordId));
|
Object.assign(record, await fetchSomeRecord(recordId));
|
||||||
});
|
});
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that this example does not update the record value whenever props are
|
Note that this example does not update the record value whenever props are
|
||||||
updated. For that situation, we need to use the `onWillUpdateProps` hook.
|
updated. For that situation, we need to use the `onWillUpdateProps` hook.
|
||||||
|
|
||||||
### `onWillUpdateProps`
|
### `onWillUpdateProps`
|
||||||
|
|
||||||
@@ -241,17 +241,17 @@ useful to perform some asynchronous task such as fetching updated data.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
function useLoader() {
|
function useLoader() {
|
||||||
const component = Component.current;
|
const component = Component.current;
|
||||||
const record = useState({});
|
const record = useState({});
|
||||||
|
|
||||||
async function updateRecord(id) {
|
async function updateRecord(id) {
|
||||||
Object.assign(record, await fetchSomeRecord(id));
|
Object.assign(record, await fetchSomeRecord(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
onWillStart(() => updateRecord(component.props.id));
|
onWillStart(() => updateRecord(component.props.id));
|
||||||
onWillUpdateProps(nextProps => updateRecord(nextProps.id));
|
onWillUpdateProps(nextProps => updateRecord(nextProps.id));
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -359,7 +359,7 @@ getters. See the [store documentation](store.md) for more information.
|
|||||||
### Making customized hooks
|
### Making customized hooks
|
||||||
|
|
||||||
Hooks are a wonderful way to organize the code of a complex component by feature
|
Hooks are a wonderful way to organize the code of a complex component by feature
|
||||||
instead of by lifecycle methods. They are like mixins, except that they can be
|
instead of by lifecycle methods. They are like mixins, except that they can be
|
||||||
easily composed together.
|
easily composed together.
|
||||||
|
|
||||||
But, like every good things in life, hooks should be used with moderation. They are
|
But, like every good things in life, hooks should be used with moderation. They are
|
||||||
@@ -373,16 +373,16 @@ not the solution to every problem.
|
|||||||
// maybe overkill
|
// maybe overkill
|
||||||
class A extends Component {
|
class A extends Component {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
useMySpecificHook()
|
useMySpecificHook();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ok
|
// ok
|
||||||
class B extends Component {
|
class B extends Component {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
this.performSpecificTask()
|
this.performSpecificTask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -391,7 +391,7 @@ not the solution to every problem.
|
|||||||
|
|
||||||
- they may be harder to test: if a customized hook injects some external side
|
- they may be harder to test: if a customized hook injects some external side
|
||||||
effect dependency, then it is harder to test without doing some non obvious
|
effect dependency, then it is harder to test without doing some non obvious
|
||||||
manipulation. For example, assume that we want to give a reference to a
|
manipulation. For example, assume that we want to give a reference to a
|
||||||
router in a `useRouter` hook. We could do this:
|
router in a `useRouter` hook. We could do this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -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.
|
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
|
A better way would be to do something like this: get the reference from the
|
||||||
@@ -410,7 +410,7 @@ not the solution to every problem.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
function useRouter() {
|
function useRouter() {
|
||||||
return Component.current.env.router;
|
return Component.current.env.router;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+38
-6
@@ -1,10 +1,16 @@
|
|||||||
# 🦉 Observer 🦉
|
# 🦉 Observer 🦉
|
||||||
|
|
||||||
Owl needs to be able to react to state changes. For example, whenever the state
|
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
|
of a component is changed, Owl needs to rerender it. To help with that, there is
|
||||||
an Observer class. Its job is to observe some object state, and react to any
|
an Observer class. Its job is to observe the state of an object (or array), and
|
||||||
change. To do that, it recursively replaces all keys of the observed state by
|
to react to any change. The observer is implemented with the native `Proxy`
|
||||||
getters and setters.
|
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:
|
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;
|
obj.a.b = 2;
|
||||||
```
|
```
|
||||||
|
|
||||||
The observer is implemented with the native `Proxy` object. Note that this
|
This example shows that an observer can observe nested properties.
|
||||||
means that it will not work on older browsers.
|
|
||||||
|
## 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.
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ owl
|
|||||||
whenReady
|
whenReady
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Note that for convenience, the `useState` hook is also exported at the root of the `owl` object.
|
Note that for convenience, the `useState` hook is also exported at the root of the `owl` object.
|
||||||
|
|
||||||
## Reference
|
## Reference
|
||||||
|
|||||||
+5
-7
@@ -38,14 +38,14 @@ const actions = {
|
|||||||
state.todos.push({
|
state.todos.push({
|
||||||
id: state.nextId++,
|
id: state.nextId++,
|
||||||
message,
|
message,
|
||||||
isCompleted: false,
|
isCompleted: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
todos: [],
|
todos: [],
|
||||||
nextId: 1,
|
nextId: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
const store = new owl.Store({ state, actions });
|
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:
|
The first argument to an action method is an object with four keys:
|
||||||
|
|
||||||
- `state`: the current state of the store content,
|
- `state`: the current state of the store content,
|
||||||
- `dispatch`: a function that can be used to dispatch other actions,
|
- `dispatch`: a function that can be used to dispatch other actions,
|
||||||
- `getters`: an object containing all getters defined in the store,
|
- `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.
|
Getters take _at most_ one argument.
|
||||||
|
|
||||||
Note that getters are cached if they don't take any argument, or their argument
|
Note that getters are not cached.
|
||||||
is a string or a number.
|
|
||||||
|
|
||||||
### Connecting a Component
|
### 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,
|
- `useDispatch` to get a reference to a dispatch function,
|
||||||
- `useGetters` to get a reference to the getters defined in the store.
|
- `useGetters` to get a reference to the getters defined in the store.
|
||||||
|
|
||||||
|
|
||||||
Assume we have this store:
|
Assume we have this store:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@@ -223,7 +222,7 @@ const actions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
counter: {value: 0}
|
counter: { value: 0 }
|
||||||
};
|
};
|
||||||
const store = new owl.Store({ state, actions });
|
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
|
- 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).
|
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
|
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 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
|
will perform a strict equality check and will update the component every time this
|
||||||
|
|||||||
+22
-22
@@ -12,7 +12,6 @@ functions are all available in the `owl.utils` namespace.
|
|||||||
- [`debounce`](#debounce): limiting rate of function calls
|
- [`debounce`](#debounce): limiting rate of function calls
|
||||||
- [`shallowEqual`](#shallowequal): shallow object comparison
|
- [`shallowEqual`](#shallowequal): shallow object comparison
|
||||||
|
|
||||||
|
|
||||||
## `whenReady`
|
## `whenReady`
|
||||||
|
|
||||||
The function `whenReady` returns a `Promise` resolved when the DOM is ready (if
|
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).
|
argument, it executes it as soon as the DOM ready (or directly).
|
||||||
|
|
||||||
```js
|
```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 qweb = new owl.QWeb(templates);
|
||||||
const app = new App({ qweb });
|
const app = new App({ qweb });
|
||||||
app.mount(document.body);
|
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.
|
previously loaded (or currently being loaded), and prevent doing twice the work.
|
||||||
|
|
||||||
For example, it is useful for lazy loading external libraries:
|
For example, it is useful for lazy loading external libraries:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
class MyComponent extends owl.Component {
|
class MyComponent extends owl.Component {
|
||||||
willStart() {
|
willStart() {
|
||||||
@@ -55,7 +55,7 @@ class MyComponent extends owl.Component {
|
|||||||
|
|
||||||
## `loadFile`
|
## `loadFile`
|
||||||
|
|
||||||
`loadFile` is a helper function to fetch a file. It simply
|
`loadFile` is a helper function to fetch a file. It simply
|
||||||
performs a `GET` request and returns the resulting string in a promise. The
|
performs a `GET` request and returns the resulting string in a promise. The
|
||||||
initial usecase for this function is to load a template file. For example:
|
initial usecase for this function is to load a template file. For example:
|
||||||
|
|
||||||
@@ -73,38 +73,38 @@ string. It does not add a `script` tag or any other side effect.
|
|||||||
## `escape`
|
## `escape`
|
||||||
|
|
||||||
Sometimes, we need to display dynamic data (for example user-generated data) in
|
Sometimes, we need to display dynamic data (for example user-generated data) in
|
||||||
the user interface. If this is done by a `QWeb` template, it is not an issue:
|
the user interface. If this is done by a `QWeb` template, it is not an issue:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<div><t t-esc="user.data"/></div>
|
<div><t t-esc="user.data"/></div>
|
||||||
```
|
```
|
||||||
|
|
||||||
The `QWeb` engine will create a `div` node and add the content of the `user.data`
|
The `QWeb` engine will create a `div` node and add the content of the `user.data`
|
||||||
string as a text node, so the web browser will not parse it as html. However,
|
string as a text node, so the web browser will not parse it as html. However,
|
||||||
it may be a problem if this is done with some javascript code like this:
|
it may be a problem if this is done with some javascript code like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
class BadComponent extends Component {
|
class BadComponent extends Component {
|
||||||
// some template with a ref to a div
|
// some template with a ref to a div
|
||||||
// some code ...
|
// some code ...
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.divRef.el.innerHTML = this.state.value;
|
this.divRef.el.innerHTML = this.state.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In this case, the content of the `div` will be parsed as html, which may inject
|
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
|
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
|
string into an escaped version of the same string, which will be properly displayed
|
||||||
by the browser, but which will not be parsed as html (for example, `"<ok>"` is
|
by the browser, but which will not be parsed as html (for example, `"<ok>"` is
|
||||||
escaped to the string: `"<ok>"`). So, the bad example above can be fixed
|
escaped to the string: `"<ok>"`). So, the bad example above can be fixed
|
||||||
with the following change:
|
with the following change:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
this.divRef.el.innerHTML = owl.utils.escape(this.state.value);
|
this.divRef.el.innerHTML = owl.utils.escape(this.state.value);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## `debounce`
|
## `debounce`
|
||||||
|
|
||||||
The `debounce` function is useful when we want to limit the number of times some
|
The `debounce` function is useful when we want to limit the number of times some
|
||||||
@@ -112,31 +112,31 @@ function/action is perfomed. For example, this may be useful to prevent issue
|
|||||||
with people double clicking on a button.
|
with people double clicking on a button.
|
||||||
|
|
||||||
It takes three arguments:
|
It takes three arguments:
|
||||||
|
|
||||||
- `func` (function): this is the function that will be rate limited
|
- `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
|
- `wait` (number): this is the number of milliseconds that we want to use to
|
||||||
rate limit the function `func`
|
rate limit the function `func`
|
||||||
- `immediate` (optional, boolean, default=false): if `immediate` is true, the
|
- `immediate` (optional, boolean, default=false): if `immediate` is true, the
|
||||||
function will be triggered immediately (leading edge of the interval). If false,
|
function will be triggered immediately (leading edge of the interval). If false,
|
||||||
the function will be triggered at the end (trailing edge).
|
the function will be triggered at the end (trailing edge).
|
||||||
|
|
||||||
It returns a function. For example:
|
It returns a function. For example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const debounce = owl.utils.debounce
|
const debounce = owl.utils.debounce;
|
||||||
window.addEventListener('mousemove', debounce(doSomething, 100));
|
window.addEventListener("mousemove", debounce(doSomething, 100));
|
||||||
```
|
```
|
||||||
|
|
||||||
As this example shows, it is usualy useful for event handlers which are triggered
|
As this example shows, it is usualy useful for event handlers which are triggered
|
||||||
very quickly, such as `scroll` or `mousemove` events.
|
very quickly, such as `scroll` or `mousemove` events.
|
||||||
|
|
||||||
|
|
||||||
## `shallowEqual`
|
## `shallowEqual`
|
||||||
|
|
||||||
This function checks if two objects have the same values assigned to each keys:
|
This function checks if two objects have the same values assigned to each keys:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
shallowEqual({a:1, b: 2}, {a:1, b:2}); // true
|
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: 3 }); // false
|
||||||
```
|
```
|
||||||
|
|
||||||
However, for performance reasons, it assumes that the two objects have the same
|
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:
|
will work:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const completeShallowEqual = (a,b) => shallowEqual(a,b) && shallowEqual(b,a);
|
const completeShallowEqual = (a, b) => shallowEqual(a, b) && shallowEqual(b, a);
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user