diff --git a/README.md b/README.md index 55f62878..fd09fcfc 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Owl is currently mostly stable. Possible future changes are explained in the Here is a short example to illustrate interactive components: ```javascript -const { Component, QWeb, useState } = owl; +const { Component, useState } = owl; const { xml } = owl.tags; class Counter extends Component { @@ -50,7 +50,7 @@ class App extends Component { static components = { Counter }; } -const app = new App({ qweb: new QWeb() }); +const app = new App(); app.mount(document.body); ``` @@ -92,7 +92,7 @@ A complete documentation for Owl can be found here: The most important sections are: -- [Quick Start](doc/quick_start.md) +- [Quick Start](doc/learning/quick_start.md) - [Component](doc/reference/component.md) - [Hooks](doc/reference/hooks.md) @@ -127,23 +127,6 @@ Owl components in an application are used to define a (dynamic) tree of componen C D ``` -**Environment:** the root component is special: it is created with an environment, -which should contain a [`QWeb` instance](doc/reference/qweb.md). 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: diff --git a/doc/comparison.md b/doc/comparison.md index 800427ff..4084e159 100644 --- a/doc/comparison.md +++ b/doc/comparison.md @@ -274,7 +274,8 @@ class Counter extends Component { dispatch = useDispatch(); } -const counter = new Counter({ store, qweb }); +owl.config.env.store = store; +const counter = new Counter(); ``` ## Hooks diff --git a/doc/learning/environment.md b/doc/learning/environment.md new file mode 100644 index 00000000..8da07aa5 --- /dev/null +++ b/doc/learning/environment.md @@ -0,0 +1,48 @@ +# 🦉 Environment 🦉 + +An environment is an object which contains a [`QWeb` instance](../reference/qweb.md). +Whenever a root component is created, it is assigned an environment (see the +reference section on [environment](../reference/environment.md). This environment +is then automatically given to each sub components (and accessible in the `this.env` property). + +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 cases for the environment is: + +- some configuration keys, +- session information, +- 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. + +For example: + + +```js +async function myEnv() { + const templates = await loadTemplates(); + const qweb = new QWeb({templates}); + const session = getSession(); + + return { + _t: myTranslateFunction, + session: session, + qweb: qweb, + services: { + localStorage: localStorage, + rpc: rpc, + }, + debug: false, + inMobileMode: true, + }; +} + +async function start() { + owl.config.env = await myEnv(); + const app = new App(); + await app.mount(document.body); +} +``` \ No newline at end of file diff --git a/doc/readme.md b/doc/readme.md index af98a1b9..8a1dcb96 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -12,6 +12,9 @@ owl QWeb Store useState + config + mode + env core EventBus Observer @@ -48,11 +51,18 @@ owl Note that for convenience, the `useState` hook is also exported at the root of the `owl` object. +## Learning Resources + +- [Quick Start: create an (almost) empty Owl application](learning/quick_start.md) +- [Environment: what it is and what it should contain](learning/environment.md) + ## Reference - [Animations](reference/animations.md) - [Component](reference/component.md) +- [Configuration](reference/config.md) - [Context](reference/context.md) +- [Environment](reference/environment.md) - [Event Bus](reference/event_bus.md) - [Hooks](reference/hooks.md) - [Misc](reference/misc.md) @@ -63,10 +73,6 @@ Note that for convenience, the `useState` hook is also exported at the root of t - [Tags](reference/tags.md) - [Utils](reference/utils.md) -## Learning Resources - -- [Quick Start: create an (almost) empty Owl application](learning/quick_start.md) - ## Miscellaneous - [Comparison with React/Vue](comparison.md) diff --git a/doc/reference/component.md b/doc/reference/component.md index 91a49307..35f05ed4 100644 --- a/doc/reference/component.md +++ b/doc/reference/component.md @@ -11,7 +11,6 @@ - [Methods](#methods) - [Lifecycle](#lifecycle) - [Root Component](#root-component) - - [Environment](#environment) - [Composition](#composition) - [Event Handling](#event-handling) - [Form Input Bindings](#form-input-bindings) @@ -81,8 +80,8 @@ a state object is defined, by using the `useState` hook. It is not mandatory to ## Reference An Owl component is a small class which represents a component or some UI element. -It exists in the context of an environment (`env`), which is propagated from a -parent to its children. The environment needs to have a QWeb instance, which +It exists in the context of an [environment](environment.md) (`env`), which is propagated from a +parent to its children. The environment needs to have a [QWeb](qweb.md) instance, which will be used to render the component template. Be aware that the name of the component may be significant: if a component does @@ -441,49 +440,12 @@ of an Owl application has to be created manually: ```js class App extends owl.Component { ... } -const qweb = new owl.QWeb({ templates: TEMPLATES }); -const env = { qweb: qweb }; -const app = new App(env); +const app = new App(); app.mount(document.body); ``` -The root component needs an environment. - -### Environment - -In Owl, an environment is an object with a `qweb` key, which has to be a -[QWeb](qweb.md) instance. This qweb instance will be used to render everything. - -The environment is meant to contain (mostly) static global information and -methods for the whole application. For example, settings keys (`mode` to determine -if we are in desktop or mobile mode, or `theme`: dark or light), `rpc` methods, -session information, ... - -The environment will be given to each child, unchanged, in the `env` property. -This can be very useful to share common information/methods. For example, all -rpcs can be made through a `rpc` method in the environment. This makes it very -easy to test a component. - -Updating the environment is not as simple as changing a component's state: its -content is not observed, so updates will not be reflected immediately in the -user interface. There is however a mechanism to force root widgets to rerender -themselves whenever the environment is modified: one only needs to call the -`forceUpdate` method on the QWeb instance. For example, a responsive environment -could be done like this: - -```js -function setupResponsivePlugin(env) { - const isMobile = () => window.innerWidth <= 768; - env.isMobile = isMobile(); - const updateEnv = owl.utils.debounce(() => { - if (env.isMobile !== isMobile()) { - env.isMobile = !env.isMobile; - env.qweb.forceUpdate(); - } - }, 15); - window.addEventListener("resize", updateEnv); -} -``` +The root component does not have a parent nor props. It will be setup with an +[environment](environment.md) (located in `owl.config.env`). ### Composition @@ -986,7 +948,7 @@ of the props. Here is how it works in Owl: - `props` key is a static key (so, different from `this.props` in a component instance) - it is optional: it is ok for a component to not define a `props` key. - props are validated whenever a component is created/updated -- props are only validated in `dev` mode (see [tooling page](../tooling.md#development-mode)) +- props are only validated in `dev` mode (see [config page](config.md#mode)) - if a key does not match the description, an error is thrown - it validates keys defined in (static) `props`. Additional keys given by the parent will cause an error. diff --git a/doc/reference/config.md b/doc/reference/config.md new file mode 100644 index 00000000..5d151539 --- /dev/null +++ b/doc/reference/config.md @@ -0,0 +1,56 @@ +# 🦉 Config 🦉 + +The Owl framework is designed to work in many situations. However, it is +sometimes necessary to customize some behaviour. This is done by using the +global `config` object. It currently has two keys: + +- [`mode`](#mode), +- [`env`](#env). + +## Mode + +By default, Owl is in _production_ mode, this means that it will try to do its +job fast, and skip some expensive operations. However, it is sometimes necessary +to have better information on what is going on, this is the purpose +of the `dev` mode. + +Owl has a mode flag, in `owl.config.mode`. Its default value is `prod`, but +it can be set to `dev`: + +```js +owl.config.mode = "dev"; +``` + +Note that templates compiled with the `prod` settings will not be recompiled. +So, changing this setting is best done at startup. + +An important job done by the `dev` mode is to validate props for each component +creation and update. Also, extra props will cause an error. + +## Env + +An Owl application needs an [environment](environment.md) to be executed. The +environment has an important key: the [QWeb](qweb.md) instance, which will render +all templates. + +Whenever a root component is mounted, Owl will take the environment from +`owl.config.env` and use it to setup the component (and its children). + +- if no environment was setup, an empty environment will be generated, +- if an environment exists, but does not have a QWeb key, a new QWeb instance + will then be added to the environment. + +The correct way to customize an environment is to simply modify `owl.config.env` +before the first component is created: + +```js +owl.config.env = { + _t: myTranslateFunction, + user: {...}, + services: { + ... + }, +}; +const app = new App(); +app.mount(document.body); +``` diff --git a/doc/reference/context.md b/doc/reference/context.md index 624d5b01..abdbec0a 100644 --- a/doc/reference/context.md +++ b/doc/reference/context.md @@ -28,10 +28,7 @@ context, and add it to the environment: ```js const deviceContext = new Context({ isMobile: true }); -const env = { - qweb: new QWeb({ templates: TEMPLATES }), - deviceContext -}; +owl.config.env.deviceContext = deviceContext; ``` If we want to make it completely responsive, we need to update its value whenever diff --git a/doc/reference/environment.md b/doc/reference/environment.md new file mode 100644 index 00000000..40e6aa10 --- /dev/null +++ b/doc/reference/environment.md @@ -0,0 +1,18 @@ +# 🦉 Environment 🦉 + +An environment is an object which contains a [`QWeb` instance](qweb.md). Whenever a root component is created, it is assigned an environment. This environment +is then automatically given to each sub component (and accessible in the `this.env` property). + +``` + Root + / \ + A B +``` + +This way, all components share the same `QWeb` instance. + +Note: some additional information can be found here: + +- [What should go into an environment?](../learning/environment.md) +- [Customizing an environment](config.md#env) + diff --git a/doc/reference/qweb.md b/doc/reference/qweb.md index 6815cbd5..b27fabbd 100644 --- a/doc/reference/qweb.md +++ b/doc/reference/qweb.md @@ -159,7 +159,7 @@ Its API is quite simple: ``` In some way, a `QWeb` instance is the core of an Owl application. It is the only -mandatory element of an [environment](component.md#environment). As such, it +mandatory element of an [environment](environment.md). As such, it has an extra responsibility: it can act as an event bus for internal communication between Owl classes. This is the reason why `QWeb` actually extends [EventBus](event_bus.md). diff --git a/doc/reference/router.md b/doc/reference/router.md index 496b8e84..2b6e4020 100644 --- a/doc/reference/router.md +++ b/doc/reference/router.md @@ -67,6 +67,9 @@ function makeEnvironment() { await env.router.start(); return env; } + +owl.config.env = makeEnvironment(); +// create root component here ``` Notice that the router needs to be started. This is an asynchronous operation diff --git a/doc/tooling.md b/doc/tooling.md index 6e0a5841..98f1e360 100644 --- a/doc/tooling.md +++ b/doc/tooling.md @@ -3,7 +3,6 @@ ## Content - [Overview](#overview) -- [Development Mode](#development-mode) - [Playground](#playground) - [Benchmarks](#benchmarks) - [Single File Component](#single-file-component) @@ -21,26 +20,6 @@ by using a static http server. A simple python server is available in `server.py`. There is also a npm script to start it: `npm run tools` (and its version with a watcher: `npm run tools:watch`). -## Development Mode - -By default, Owl is in _production_ mode, this means that it will try to do its -job fast, and skip some expensive operations. However, in some cases, it is -convenient to have better information on what is going on, this is the purpose -of the dev mode. - -Owl has a mode flag, in `owl.__info__.mode`. Its default value is `prod`, but -it can be set to `dev`: - -```js -owl.__info__.mode = "dev"; -``` - -Note that templates compiled with the `prod` settings will not be recompiled. -So, changing this setting is best done at startup. - -An important job done by the `dev` mode is to validate props for each component -creation and update. Also, extra props will cause an error. - ## Playground The playground is an important application designed to help learning and