mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[DOC] readme: add quick overview section
This commit is contained in:
@@ -4,22 +4,29 @@ _A no nonsense web framework for structured, dynamic and maintainable applicatio
|
|||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
The Odoo Web Library (OWL) is a smallish (~17kb gzipped) UI framework intended to be the basis for
|
The Odoo Web Library (OWL) is a smallish (~17kb gzipped) UI framework intended to
|
||||||
the [Odoo](https://www.odoo.com/) Web Client. OWL's main features are:
|
be the basis for the [Odoo](https://www.odoo.com/) Web Client. Owl is a modern
|
||||||
|
framework, written in Typescript, taking the best ideas from React and Vue in a
|
||||||
|
simple and consistent way. Owl's main features are:
|
||||||
|
|
||||||
- a _declarative component system_, (template based, with asynchronous rendering and a virtual dom)
|
- a declarative component system,
|
||||||
- a store implementation (for state management)
|
- a reactivity system based on hooks,
|
||||||
|
- a store implementation (for state management),
|
||||||
- a small frontend router
|
- a small frontend router
|
||||||
|
|
||||||
**Try it online!** An online playground is available at [https://odoo.github.io/owl/playground](https://odoo.github.io/owl/playground) to let you experiment with the OWL framework.
|
Owl components are defined with ES6 classes, they use QWeb templates, an underlying
|
||||||
|
virtual dom, integrates beautifully with hooks, and the rendering is asynchronous.
|
||||||
|
|
||||||
|
**Try it online!** An online playground is available at [https://odoo.github.io/owl/playground](https://odoo.github.io/owl/playground) to let you experiment with the Owl framework. There
|
||||||
|
are some code examples to showcase some interesting features.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Here is a short example to illustrate interactive components:
|
Here is a short example to illustrate interactive components:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { Component, QWeb, useState } from 'owl'
|
import { Component, QWeb, useState } from "owl";
|
||||||
import { xml } from 'owl/tags'
|
import { xml } from "owl/tags";
|
||||||
|
|
||||||
class Counter extends Component {
|
class Counter extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
@@ -48,12 +55,14 @@ const app = new App({ qweb: new QWeb() });
|
|||||||
app.mount(document.body);
|
app.mount(document.body);
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the counter component is made reactive with the [`useState` hook](doc/hooks.md#usestate).
|
Note that the counter component is made reactive with the [`useState`](doc/hooks.md#usestate)
|
||||||
|
hook. Also, all examples here uses the `xml` helper to define inline templates.
|
||||||
|
But this is not mandatory, many applications will load templates separately.
|
||||||
|
|
||||||
More interesting examples can be found on the
|
More interesting examples can be found on the
|
||||||
[playground](https://odoo.github.io/owl/playground) application.
|
[playground](https://odoo.github.io/owl/playground) application.
|
||||||
|
|
||||||
## OWL's Design Principles
|
## Design Principles
|
||||||
|
|
||||||
OWL is designed to be used in highly dynamic applications where changing
|
OWL is designed to be used in highly dynamic applications where changing
|
||||||
requirements are common and code needs to be maintained by large teams.
|
requirements are common and code needs to be maintained by large teams.
|
||||||
@@ -82,7 +91,7 @@ The complete documentation can be found [here](doc/readme.md). The most importan
|
|||||||
|
|
||||||
- [Quick Start](doc/quick_start.md)
|
- [Quick Start](doc/quick_start.md)
|
||||||
- [Component](doc/component.md)
|
- [Component](doc/component.md)
|
||||||
- [QWeb](doc/qweb.md)
|
- [Hooks](doc/hooks.md)
|
||||||
|
|
||||||
Found an issue in the documentation? A broken link? Some outdated information?
|
Found an issue in the documentation? A broken link? Some outdated information?
|
||||||
Submit a PR!
|
Submit a PR!
|
||||||
@@ -97,14 +106,153 @@ If you want to use a simple `<script>` tag, the last release can be downloaded h
|
|||||||
Some npm scripts are available:
|
Some npm scripts are available:
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
| --------------------- | ---------------------------------------------------------------------------- |
|
| ---------------- | -------------------------------------------------- |
|
||||||
| `npm install` | install every dependency required for this project |
|
| `npm install` | install every dependency required for this project |
|
||||||
| `npm run build` | build a bundle of _owl_ in the _/dist/_ folder |
|
| `npm run build` | build a bundle of _owl_ in the _/dist/_ folder |
|
||||||
| `npm run minify` | minify the prebuilt owl.js file |
|
| `npm run minify` | minify the prebuilt owl.js file |
|
||||||
| `npm run test` | run all tests |
|
| `npm run test` | run all (owl) tests |
|
||||||
| `npm run test:watch` | run all tests, and keep a watcher |
|
|
||||||
| `npm run tools` | build tools applications, start a static server (see [here](doc/tooling.md)) |
|
## Quick Overview
|
||||||
| `npm run tools:watch` | same as `tools`, but with a watcher to rebuild owl |
|
|
||||||
|
Owl components in an application are used to define a (dynamic) tree of components.
|
||||||
|
|
||||||
|
```
|
||||||
|
Root
|
||||||
|
/ \
|
||||||
|
A B
|
||||||
|
/ \
|
||||||
|
C D
|
||||||
|
```
|
||||||
|
|
||||||
|
**Environment:** the root component is special: it is created with an environment,
|
||||||
|
which should contain a `QWeb` instance. The environment is then automatically
|
||||||
|
propagated to each sub components (and accessible in the `this.env` property).
|
||||||
|
|
||||||
|
```js
|
||||||
|
const env = { qweb: new QWeb() };
|
||||||
|
const app = new App(env);
|
||||||
|
app.mount(document.body);
|
||||||
|
```
|
||||||
|
|
||||||
|
The environment is mostly static. Each application is free to add anything to
|
||||||
|
the environment, which is very useful, since this can be accessed by each sub
|
||||||
|
component. Some good use case for that is some configuration keys, session
|
||||||
|
information or generic services (such as doing rpcs, or accessing local storage).
|
||||||
|
Doing it this way means that components are easily testable: we can simply
|
||||||
|
create a test environment with mock services.
|
||||||
|
|
||||||
|
**State:** each component can manage its own local state. It is a simple ES6
|
||||||
|
class, there are no special rules:
|
||||||
|
|
||||||
|
```js
|
||||||
|
class Counter extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<button t-on-click="increment">
|
||||||
|
Click Me! [<t t-esc="state.value"/>]
|
||||||
|
</button>`;
|
||||||
|
|
||||||
|
state = { value: 0 };
|
||||||
|
|
||||||
|
increment() {
|
||||||
|
this.state.value++;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The example above shows a component with a local state. Note that since there
|
||||||
|
is nothing magical to the `state` object, we need to manually call the `render`
|
||||||
|
function whenever we update it. This can quickly become annoying (and not
|
||||||
|
efficient if we do it too much). There is a better way: using the `useState`
|
||||||
|
hook, which transforms an object into a reactive version of itself:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { useState } = owl.hooks;
|
||||||
|
|
||||||
|
class Counter extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<button t-on-click="increment">
|
||||||
|
Click Me! [<t t-esc="state.value"/>]
|
||||||
|
</button>`;
|
||||||
|
|
||||||
|
state = useState({ value: 0 });
|
||||||
|
|
||||||
|
increment() {
|
||||||
|
this.state.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Props:** sub components often needs some information from their parents. This
|
||||||
|
is done by adding the required information to the template. This will then be
|
||||||
|
accessible by the sub component in the `props` object. Note that there is an
|
||||||
|
important rule here: the information contained in the `props` object is not
|
||||||
|
owned by the sub component, and should never be modified.
|
||||||
|
|
||||||
|
```js
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div>Hello <t t-esc="props.name"/></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<Child name="'Owl'" />
|
||||||
|
<Child name="'Framework'" />
|
||||||
|
</div>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Communication:** there are multiple ways to communicate information between
|
||||||
|
components. However, the two most important ways are the following:
|
||||||
|
|
||||||
|
- from parent to children: by using `props`,
|
||||||
|
- from a children to one of its parent: by triggering events.
|
||||||
|
|
||||||
|
The following example illustrate both mechanisms:
|
||||||
|
|
||||||
|
```js
|
||||||
|
class OrderLine extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div t-on-click="add">
|
||||||
|
<div><t t-esc="props.line.name"/></div>
|
||||||
|
<div>Quantity: <t t-esc="props.line.quantity"/></div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
add() {
|
||||||
|
this.trigger("add-to-order", { line: props.line });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div t-on-add-to-order="addToOrder">
|
||||||
|
<OrderLine
|
||||||
|
t-foreach="orders"
|
||||||
|
t-as="line"
|
||||||
|
line="line" />
|
||||||
|
</div>`;
|
||||||
|
static components = { OrderLine };
|
||||||
|
orders = useState([{ id: 1, name: "Coffee", quantity: 0 }, { id: 2, name: "Tea", quantity: 0 }]);
|
||||||
|
|
||||||
|
addToOrder(event) {
|
||||||
|
const line = event.detail.line;
|
||||||
|
line.quantity++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
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](doc/component.md#event-handling)
|
||||||
|
for more details on how events work.
|
||||||
|
|
||||||
|
Note that this example would have also worked if the `OrderLine` component
|
||||||
|
directly modifies the `line` object. However, this is not a good practice: this
|
||||||
|
only works because the `props` object received by the child component is reactive,
|
||||||
|
so the child component is then coupled to the parents implementation.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user