From 83d38a6f484b54e6676f2f5f6b04ad32c5220c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Thu, 13 Jan 2022 10:26:14 +0100 Subject: [PATCH] [DOC] reorganize and update documentation to owl 2 --- README.md | 67 +- doc/learning/how_to_write_sfc.md | 52 - doc/learning/tutorial_todoapp.md | 10 +- doc/miscellaneous/architecture.md | 68 ++ doc/miscellaneous/comparison.md | 4 +- doc/miscellaneous/rendering.md | 32 - doc/miscellaneous/vdom.md | 18 - doc/readme.md | 42 + doc/reference/animations.md | 127 --- doc/reference/app.md | 107 ++ doc/reference/browser.md | 33 - doc/reference/component.md | 939 +++++------------- doc/reference/concurrency_model.md | 30 +- doc/reference/config.md | 44 - doc/reference/content.md | 36 - doc/reference/environment.md | 104 +- doc/reference/error_handling.md | 92 +- doc/reference/event_handling.md | 104 +- doc/reference/hooks.md | 433 +++----- doc/reference/input_bindings.md | 92 ++ doc/reference/misc.md | 133 --- doc/reference/mounting.md | 60 -- doc/reference/observer.md | 52 - doc/reference/portal.md | 17 + doc/reference/props.md | 202 +++- doc/reference/props_validation.md | 103 -- doc/reference/qweb_engine.md | 153 --- doc/reference/reactivity.md | 80 ++ doc/reference/refs.md | 37 + doc/reference/tags.md | 184 ---- ...eb_templating_language.md => templates.md} | 257 +++-- doc/reference/translations.md | 48 + doc/reference/utils.md | 124 +-- 33 files changed, 1396 insertions(+), 2488 deletions(-) delete mode 100644 doc/learning/how_to_write_sfc.md create mode 100644 doc/miscellaneous/architecture.md delete mode 100644 doc/miscellaneous/rendering.md delete mode 100644 doc/miscellaneous/vdom.md create mode 100644 doc/readme.md delete mode 100644 doc/reference/animations.md create mode 100644 doc/reference/app.md delete mode 100644 doc/reference/browser.md delete mode 100644 doc/reference/config.md delete mode 100644 doc/reference/content.md create mode 100644 doc/reference/input_bindings.md delete mode 100644 doc/reference/misc.md delete mode 100644 doc/reference/mounting.md delete mode 100644 doc/reference/observer.md create mode 100644 doc/reference/portal.md delete mode 100644 doc/reference/props_validation.md delete mode 100644 doc/reference/qweb_engine.md create mode 100644 doc/reference/reactivity.md create mode 100644 doc/reference/refs.md delete mode 100644 doc/reference/tags.md rename doc/reference/{qweb_templating_language.md => templates.md} (73%) create mode 100644 doc/reference/translations.md diff --git a/README.md b/README.md index 7608d2b1..2f40cb5f 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,10 @@ 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, -- a reactivity system based on hooks, -- concurrent mode by default, +- a fine grained reactivity system similar to Vue, +- hooks +- fragments +- asynchronous rendering Owl components are defined with ES6 classes and xml templates, uses an underlying virtual DOM, integrates beautifully with hooks, and the rendering is @@ -38,28 +40,26 @@ const { Component, useState, mount, xml } = owl; class Counter extends Component { static template = xml` - `; state = useState({ value: 0 }); } -class App extends Component { +class Root extends Component { static template = xml` -
- Hello Owl - -
`; + Hello Owl + `; static components = { Counter }; } -mount(App, document.body); +mount(Root, document.body); ``` Note that the counter component is made reactive with the [`useState` hook](doc/reference/hooks.md#usestate). -Also, all examples here uses the [`xml` helper](doc/reference/tags.md#xml-tag) to define inline templates. +Also, all examples here uses the [`xml` helper](doc/reference/templates.md#inline-templates) to define inline templates. But this is not mandatory, many applications will load templates separately. More interesting examples can be found on the @@ -74,47 +74,42 @@ Are you new to Owl? This is the place to start! - [Tutorial: create a TodoList application](doc/learning/tutorial_todoapp.md) - [How to start an Owl project](doc/learning/quick_start.md) - [How to test Components](doc/learning/how_to_test.md) -- [How to write Single File Components](doc/learning/how_to_write_sfc.md) ### Reference -You will find here a complete reference of every feature, class or object -provided by Owl. - -- [Animations](doc/reference/animations.md) -- [Browser](doc/reference/browser.md) +- [Overview](doc/readme.md) +- [App](doc/reference/app.md) - [Component](doc/reference/component.md) -- [Content](doc/reference/content.md) +- [Component Lifecycle](doc/reference/component.md#lifecycle) - [Concurrency Model](doc/reference/concurrency_model.md) -- [Configuration](doc/reference/config.md) -- [Context](doc/reference/context.md) +- [Dynamic sub components](doc/reference/component.md#dynamic-sub-components) - [Environment](doc/reference/environment.md) -- [Event Bus](doc/reference/event_bus.md) -- [Event Handling](doc/reference/event_handling.md) - [Error Handling](doc/reference/error_handling.md) +- [Event Handling](doc/reference/event_handling.md) +- [Form Input Bindings](doc/reference/input_bindings.md) +- [Fragments](doc/reference/templates.md#fragments) - [Hooks](doc/reference/hooks.md) -- [Mounting a component](doc/reference/mounting.md) -- [Miscellaneous Components](doc/reference/misc.md) -- [Observer](doc/reference/observer.md) +- [Loading Templates](doc/reference/app.md#loading-templates) +- [Mounting a component](doc/reference/app.md#mount-helper) +- [Portal](doc/reference/portal.md) - [Props](doc/reference/props.md) -- [Props Validation](doc/reference/props_validation.md) -- [QWeb Templating Language](doc/reference/qweb_templating_language.md) -- [QWeb Engine](doc/reference/qweb_engine.md) +- [Props Validation](doc/reference/props.md#props-validation) +- [Reactivity](doc/reference/reactivity.md) +- [Rendering SVG](doc/reference/templates.md#rendering-svg) +- [Refs](doc/reference/refs.md) - [Slots](doc/reference/slots.md) -- [Tags](doc/reference/tags.md) +- [Sub components](doc/reference/component.md#sub-components) +- [Sub templates](doc/reference/templates.md#sub-templates) +- [Templates (Qweb)](doc/reference/templates.md) +- [Translations](doc/reference/translations.md) - [Utils](doc/reference/utils.md) ### Other Topics -This section provides miscellaneous document that explains some topics -which cannot be considered either a tutorial, or reference documentation. - -- [Owl architecture: the Virtual DOM](doc/miscellaneous/vdom.md) -- [Owl architecture: the rendering pipeline](doc/miscellaneous/rendering.md) +- [Notes On Owl Architecture](doc/miscellaneous/architecture.md) - [Comparison with React/Vue](doc/miscellaneous/comparison.md) -- [Why did Odoo built Owl?](doc/miscellaneous/why_owl.md) - - +- [Why did Odoo build Owl?](doc/miscellaneous/why_owl.md) +- [Changelog (from owl 1.x to 2.x)](CHANGELOG.md) ## Installing Owl diff --git a/doc/learning/how_to_write_sfc.md b/doc/learning/how_to_write_sfc.md deleted file mode 100644 index 501afdb1..00000000 --- a/doc/learning/how_to_write_sfc.md +++ /dev/null @@ -1,52 +0,0 @@ -# πŸ¦‰ How to write Single File Components πŸ¦‰ - -It is very useful to group code by feature instead of by type of file. It makes -it easier to scale application to larger size. - -To do so, Owl has two small helpers that make it easy to define a -template or a stylesheet inside a javascript (or typescript) file: the -[`xml`](../reference/tags.md#xml-tag) and [`css`](../reference/tags.md#css-tag) -helper. - -This means that the template, the style and the javascript code can be defined in -the same file. For example: - -```js -const { Component } = owl; -const { xml, css } = owl.tags; - -// ----------------------------------------------------------------------------- -// TEMPLATE -// ----------------------------------------------------------------------------- -const TEMPLATE = xml/* xml */ ` -
- - -
`; - -// ----------------------------------------------------------------------------- -// STYLE -// ----------------------------------------------------------------------------- -const STYLE = css/* css */ ` - .main { - display: grid; - grid-template-columns: 200px auto; - } -`; - -// ----------------------------------------------------------------------------- -// CODE -// ----------------------------------------------------------------------------- -class Main extends Component { - static template = TEMPLATE; - static style = STYLE; - static components = { Sidebar, Content }; - - // rest of component... -} -``` - -Note that the above example has an inline xml comment, just after the `xml` call. -This is useful for some editor plugins, such as the VS Code addon -`Comment tagged template`, which, if installed, add syntax highlighting to the -content of the template string. diff --git a/doc/learning/tutorial_todoapp.md b/doc/learning/tutorial_todoapp.md index 139179fc..e21c0620 100644 --- a/doc/learning/tutorial_todoapp.md +++ b/doc/learning/tutorial_todoapp.md @@ -116,7 +116,7 @@ class App extends Component {} App.template = xml`
todo app
`; ``` -Note 3: writing inline templates with the [`xml` helper](../reference/tags.md#xml-tag) +Note 3: writing inline templates with the [`xml` helper](../reference/templates.md#inline-templates) is nice, but there is no syntax highlighting, and this makes it very easy to have malformed xml. Some editors support syntax highlighting for this situation. For example, VS Code has an addon `Comment tagged template`, which, if installed, @@ -173,14 +173,14 @@ class Root extends Component { } ``` -The template contains a [`t-foreach`](../reference/qweb_templating_language.md#loops) loop to iterate +The template contains a [`t-foreach`](../reference/templates.md#loops) loop to iterate through the tasks. It can find the `tasks` list from the component, since the component is the rendering context. Note that we use the `id` of each task as a `t-key`, which is very common. There are two css classes: `task-list` and `task`, that we will use in the next section. Finally, notice the use of the `t-att-checked` attribute: -prefixing an attribute by [`t-att`](../reference/qweb_templating_language.md#dynamic-attributes) makes +prefixing an attribute by [`t-att`](../reference/templates.md#dynamic-attributes) makes it dynamic. Owl will evaluate the expression and set it as the value of the attribute. @@ -277,10 +277,10 @@ A lot of stuff happened here: - the `Task` component has a `props` key: this is only useful for validation purpose. It says that each `Task` should be given exactly one prop, named `task`. If this is not the case, Owl will throw an - [error](../reference/props_validation.md). This is extremely + [error](../reference/props.md#props-validation). This is extremely useful when refactoring components - finally, to activate the props validation, we need to set Owl's - [mode](../reference/config.md#mode) to `dev`. This is done in the last argument + [mode](../reference/app.md#configuration) to `dev`. This is done in the last argument of the `mount` function. Note that this should be removed when an app is used in a real production environment, since `dev` mode is slightly slower, due to extra checks and validations. diff --git a/doc/miscellaneous/architecture.md b/doc/miscellaneous/architecture.md new file mode 100644 index 00000000..b0536251 --- /dev/null +++ b/doc/miscellaneous/architecture.md @@ -0,0 +1,68 @@ +# πŸ¦‰ Notes On Owl Architecture πŸ¦‰ + +We explain here how Owl is designed + +Warning: these notes are technical by nature, and intended for people working +on Owl (or interested in understanding its design). + +## Overview + +Roughly speaking, Owl has 5 main parts: + +- a virtual dom system (in `src/blockdom`) +- a component system (in `src/component`) +- a template compiler (located in the `src/compiler` folder) +- a small runtime code to tie them together (in `src/app`) +- a reactivity system (in `src/reactivity.ts`) + +There are some other files, but the core of Owl can be understood with these +five main parts. + +The virtual dom is an optimized virtual dom based on blocks, which supports +multi blocks (for fragments). Everything that owl renders is internally +represented by a virtual node. The job of the virtual dom is to efficiently +represent the current state of the application, and to build an actual DOM +representation when needed, or update the DOM whenever it is needed. + +- some other helpers/smaller scale stuff + A rendering occurs in two phases: + +- virtual rendering: this generates the virtual dom in memory, asynchronously +- patch: applies a virtual tree to the screen (synchronously) + +There are several classes involved in a rendering: + +- components +- a scheduler +- fibers: small objects containing some metadata, associated with a rendering of + a specific component + +Components are organized in a dynamic component tree, visible in the user +interface. Whenever a rendering is initiated in a component `C`: + +- a fiber is created on `C` with the rendering props information +- the virtual rendering phase starts on C (will asynchronously render all the + child components) +- the fiber is added to the scheduler, which will poll continuously, every + animation frame, if the fiber is done +- once it is done, the scheduler will call the task callback, which will apply + the patch (if it was not cancelled in the meantime). + +# πŸ¦‰ VDom πŸ¦‰ + +Owl is a declarative component system: we declare the structure of the component +tree, and Owl will translate that to a list of imperative operations. This +translation is done by a virtual dom. This is the low level layer of Owl, most +developer will not need to call directly the virtual dom functions. + +The main idea behind a virtual dom is to keep a in-memory representation of the +DOM (called a virtual node), and whenever some change is needed, to regenerate +a new representation, compute the difference between the old and the new, then +apply the changes. + +`vdom` exports two functions: + +- `h`: create a new virtual node +- `patch`: compare two virtual nodes, and apply the difference. + +Note: Owl's virtual dom is a fork of [snabbdom](https://github.com/snabbdom/snabbdom). diff --git a/doc/miscellaneous/comparison.md b/doc/miscellaneous/comparison.md index 6183322f..4b8b7eb3 100644 --- a/doc/miscellaneous/comparison.md +++ b/doc/miscellaneous/comparison.md @@ -78,7 +78,7 @@ additional tools, we made a lot of effort to make the most of the web platform. For example, Owl uses the standard `xml` parser that comes with every browser. Because of that, Owl did not have to write its own template parser. Another -example is the [`xml`](../reference/tags.md#xml-tag) tag helper function, which makes use of +example is the [`xml`](../reference/templates.md#inline-templates) tag helper function, which makes use of native template literals to allow in a natural way to write `xml` templates directly in the javascript code. This can be easily integrated with editor plugins to have autocompletion inside the template. @@ -126,7 +126,7 @@ structured than a template language. Note that the tooling is quite impressive: there is a syntax highlighter for jsx here on github! By comparison, here is the equivalent Owl component, written with the -[`xml`](../reference/tags.md#xml-tag) tag helper: +[`xml`](../reference/templates.md#inline-templates) tag helper: ```js class Clock extends Component { diff --git a/doc/miscellaneous/rendering.md b/doc/miscellaneous/rendering.md deleted file mode 100644 index fe2df172..00000000 --- a/doc/miscellaneous/rendering.md +++ /dev/null @@ -1,32 +0,0 @@ -# πŸ¦‰ Rendering Pipeline πŸ¦‰ - -We explain here how Owl is designed, from the perspective of its rendering -pipeline. - -Warning: these notes are technical by nature, and intended for people working -on Owl (or interested in understanding its design). - -## Overview - -A rendering occurs in two phases: - -- virtual rendering: this generates the virtual dom in memory, asynchronously -- patch: applies a virtual tree to the screen (synchronously) - -There are several classes involved in a rendering: - -- components -- a scheduler -- fibers: small objects containing some metadata, associated with a rendering of - a specific component - -Components are organized in a dynamic component tree, visible in the user -interface. Whenever a rendering is initiated in a component `C`: - -- a fiber is created on `C` with the rendering props information -- the virtual rendering phase starts on C (will asynchronously render all the - child components) -- the fiber is added to the scheduler, which will poll continuously, every - animation frame, if the fiber is done -- once it is done, the scheduler will call the task callback, which will apply - the patch (if it was not cancelled in the meantime). diff --git a/doc/miscellaneous/vdom.md b/doc/miscellaneous/vdom.md deleted file mode 100644 index 50975c8b..00000000 --- a/doc/miscellaneous/vdom.md +++ /dev/null @@ -1,18 +0,0 @@ -# πŸ¦‰ VDom πŸ¦‰ - -Owl is a declarative component system: we declare the structure of the component -tree, and Owl will translate that to a list of imperative operations. This -translation is done by a virtual dom. This is the low level layer of Owl, most -developer will not need to call directly the virtual dom functions. - -The main idea behind a virtual dom is to keep a in-memory representation of the -DOM (called a virtual node), and whenever some change is needed, to regenerate -a new representation, compute the difference between the old and the new, then -apply the changes. - -`vdom` exports two functions: - -- `h`: create a new virtual node -- `patch`: compare two virtual nodes, and apply the difference. - -Note: Owl's virtual dom is a fork of [snabbdom](https://github.com/snabbdom/snabbdom). diff --git a/doc/readme.md b/doc/readme.md new file mode 100644 index 00000000..0528c2b7 --- /dev/null +++ b/doc/readme.md @@ -0,0 +1,42 @@ +# πŸ¦‰ Owl overview πŸ¦‰ + +Here is a list of everything exported by the Owl library: + +Main entities: + +- [`App`](reference/app.md): represent an Owl application (mainly a root component,a set of templates, and a config) +- [`Component`](reference/component.md): the main class to define a concrete Owl component +- [`mount`](reference/app.md#mount-helper): main entry point for most application: mount a component to a target +- [`useState`](reference/reactivity.md#usestate): create a reactive object (hook, linked to a specific component) +- [`reactive`](reference/reactivity.md#reactive): create a reactive object (not linked to any component) +- [`xml`](reference/templates.md#inline-templates): helper to define an inline template + +Lifecycle hooks: + +- [`onWillStart`](reference/component.md#willstart): hook to define asynchronous code that should be executed before component is rendered +- [`onMounted`](reference/component.md#mounted): hook to define code that should be executed when component is mounted +- [`onWillPatch`](reference/component.md#willpatch): hook to define code that should be executed before component is patched +- [`onWillUpdateProps`](reference/component.md#willupdateprops): hook to define code that should be executed before component is updated +- [`onPatched`](reference/component.md#patched): hook to define code that should be executed when component is patched +- [`onWillRender`](reference/component.md#willrender): hook to define code that should be executed before component is rendered +- [`onRendered`](reference/component.md#rendered): hook to define code that should be executed after component is rendered +- [`onWillUnmount`](reference/component.md#willunmount): hook to define code that should be executed before component is unmounted +- [`onWillDestroy`](reference/component.md#willdestroy): hook to define code that should be executed before component is destroyed +- [`onError`](reference/component.md#onerror): hook to define a Owl error handler + +Other hooks: + +- [`useComponent`](reference/hooks.md#usecomponent): return a reference to the current component (useful to create derived hooks) +- [`useEffect`](reference/hooks.md#useeffect): define an effect with its dependencies +- [`useEnv`](reference/hooks.md#useenv): return a reference to the current env +- [`useExternalListener`](reference/hooks.md#useexternallistener): add a listener outside of a component DOM +- [`useRef`](reference/hooks.md#useref): get an object representing a reference (`t-ref`) +- [`useSubEnv`](reference/hooks.md#usesubenv): extend the current env with additional information for child components + +Utility/helpers: + +- [`EventBus`](reference/utils.md#eventbus): a simple event bus +- [`loadFile`](reference/utils.md#loadfile): an helper to load a file from the server +- [`markup`](reference/templates.md#outputting-data): utility function to define strings that represent html (should not be escaped) +- [`status`](reference/component.md#status-helper): utility function to get the status of a component (new, mounted or destroyed) +- [`whenReady`](reference/utils.md#whenready): utility function to execute code when DOM is ready diff --git a/doc/reference/animations.md b/doc/reference/animations.md deleted file mode 100644 index 3a4ff190..00000000 --- a/doc/reference/animations.md +++ /dev/null @@ -1,127 +0,0 @@ -# πŸ¦‰ Animations πŸ¦‰ - -Animation is a complex topic. There are many different use cases, and many -solutions and technologies. Owl only supports some basic use cases. - -## Simple CSS effects - -Sometimes, using pure CSS is enough. For these use cases, Owl is not really -necessary: it just needs to render a DOM element with a specific class. For -example: - -```xml -Click -``` - -with the following CSS: - -```css -btn { - background-color: gray; -} - -.flash { - transition: background 0.5s; -} - -.flash:active { - background-color: #41454a; - transition: background 0s; -} -``` - -will produce a nice flash effect whenever the user clicks (or activates with the -keyboard) the button. - -## CSS Transitions - -A more complex situation occurs when we want to transition an element in or out -of the page. For example, we may want a fade-in and fade-out effect. - -The `t-transition` directive is here to help us. It works on html elements and -on components, by adding and removing some css classes. - -To perform useful transition effects, whenever an element appears or disappears, -it is necessary to add/remove some css style or class at some precise moment in -the lifetime of a node. Since this is not easy to do by hand, Owl `t-transition` -directive is there to help. - -Whenever a node has a `t-transition` directive, with a `name` value, the following -sequence of events will happen: - -At node insertion: - -- the css classes `name-enter` and `name-enter-active` will be added directly - when the node is inserted into the DOM. -- on the next animation frame: the css class `name-enter` will be removed and the - class `name-enter-to` will be added (so they can be used to trigger css - transition effects). -- at the end of the transition, `name-enter-to` and `name-enter-active` will be removed. - -At node destruction: - -- the css classes `name-leave` and `name-leave-active` will be added before the - node is removed to the DOM. -- on the next animation frame: the css class `name-leave` will be removed and the - class `name-leave-to` will be added (so they can be used to trigger css - transition effects). -- at the end of the transition, `name-leave-to` and `name-leave-active` will be removed. - -For example, a simple fade in/out effect can be done with this: - -```xml -
-
Hello
-
-``` - -```css -.fade-enter-active, -.fade-leave-active { - transition: opacity 0.5s; -} -.fade-enter, -.fade-leave-to { - opacity: 0; -} -``` - -The `t-transition` directive can be applied on a node element or on a component. - -Notes: - -Owl does not support more than one transition on a single node, so the -`t-transition` expression must be a single value (i.e. no space allowed). - -## SCSS Mixins - -If you use SCSS, you can use mixins to make generic animations. Here is an exemple with a fade in / fade out animation: - -```scss -@mixin animation-fade($time, $name) { - .#{$name}_fade-enter-active, - .#{$name}_fade-active { - transition: all $time; - } - - .#{$name}_fade-enter { - opacity: 0; - } - - .#{$name}_fade-leave-to { - opacity: 0; - } -} -``` - -Usage: - -```scss -@include animation-fade(0.5s, "o_notification"); -``` - -You can now have in your template: - -```xml - -``` diff --git a/doc/reference/app.md b/doc/reference/app.md new file mode 100644 index 00000000..c1ddc820 --- /dev/null +++ b/doc/reference/app.md @@ -0,0 +1,107 @@ +# πŸ¦‰ App πŸ¦‰ + +## Content + +- [Overview](#overview) +- [Configuration](#configuration) +- [`mount` helper](#mount-helper) +- [Loading templates](#loading-templates) + +## Overview + +Every Owl application has a root element, a set of templates, an environment and +possibly a few other settings. The `App` class is a simple class that represents +all of these elements. Here is an example: + +```js +const {Component, App } = owl; + +class MyComponent extends Component { ... } + +const app = new App(MyComponent, { props: {...}, templates: "..."}); +app.mount(document.body); +``` + +The basic workflow is: create an `App` instance configured with the root +component, the templates, and possibly other settings. Then, we mount that +instance somewhere in the DOM. + +## Configuration + +- **`constructor(Root[, config])`**: first argument should be a component class (not + an instance), and the optional second argument is a configuration object. + + The `config` object is an object with some of the following keys: + + - **`env (object)`**: if given, this will be the shared `env` given to each component + - **`props (object)`**: the props given to the root component + - **`dev (boolean, default=false)`**: if `true`, the application is rendered in `dev` + mode, which activates some additional checks (in particular, the props validation + code is only performed in dev mode) + - **`translatableAttributes (string[])`**: a list of additional attributes that should + be translated (see [translations](translations.md)) + - **`translateFn (function)`**: a function that will be called by owl to translate + templates (see [translations](translations.md)) + - **`templates (string | xml document)`**: all the templates that will be used by + the components created by the application. + +- **`mount(target, options)`**: first argument is an html element, and the optional + second argument is an object with mounting options (see below). Mount the app + to a target in the DOM. Note that this is an asynchronous operation: the `mount` + method returns a promise that resolves to the component instance whenever it + is complete. + + The `option` object is an object with the following keys: + + - **`position (string)`**: either `first-child` or `last-child`. This option determines + the position of the application in the target: either first or last child. + +- **`destroy()`**: destroys the application + +## `mount` helper + +Note that there is a `mount` helper to do that in just a line: + +```js +const { mount, Component } = owl; + +class MyComponent extends Component { + ... +} + +mount(MyComponent, document.body, { props: {...}, templates: "..."}); +``` + +Here is the `mount` function signature: + +**`mount(Component, target, config)`** with the following arguments: + +- **`Component`**: a component class (Root component of the app) +- **`target`**: an html element, where the component will be mounted as last child +- **`config (optional)`**: a config object (the same as the App config object) + +Most of the time, the `mount` helper is more convenient, but whenever one needs +a reference to the actual Owl App, then using the `App` class directly is +possible. + +## Loading templates + +Most applications will need to load templates whenever they start. Here is +what it could look like in practice: + +```js +// in the main js file: +const { loadFile, mount } = owl; + +// async, so we can use async/await +(async function setup() { + const templates = await loadFile(`/some/endpoint/that/return/templates`); + const env = { + _t: someTranslateFn, + templates, + // possibly other stuff + }; + + mount(Root, document.body, { env }); +})(); +``` diff --git a/doc/reference/browser.md b/doc/reference/browser.md deleted file mode 100644 index fd7b3724..00000000 --- a/doc/reference/browser.md +++ /dev/null @@ -1,33 +0,0 @@ -# πŸ¦‰ Browser πŸ¦‰ - -## Content - -- [Overview](#overview) -- [Browser Content](#browser-content) - -## Overview - -The browser object contains some browser native APIs, such as `setTimeout`, that -are used by Owl and its utility functions. They are exposed with the intent of -making them mockable if necessary. - -```js -owl.browser.setTimeout === window.setTimeout; // return true -``` - -For now, this object contains some functions that are not used by Owl. They -will eventually be removed in Owl 2.0. - -## Browser Content - -More specifically, the `browser` object contains the following methods and objects: - -- `setTimeout` -- `clearTimeout` -- `setInterval` -- `clearInterval` -- `requestAnimationFrame` -- `random` -- `Date` -- `fetch` -- `localStorage` diff --git a/doc/reference/component.md b/doc/reference/component.md index bde94d20..951dae33 100644 --- a/doc/reference/component.md +++ b/doc/reference/component.md @@ -1,185 +1,64 @@ -# πŸ¦‰ OWL Component πŸ¦‰ +# πŸ¦‰ Owl Component πŸ¦‰ ## Content - [Overview](#overview) -- [Example](#example) -- [Reference](#reference) - - [Reactive System](#reactive-system) - - [Properties](#properties) - - [Static Properties](#static-properties) - - [Methods](#methods) - - [Lifecycle](#lifecycle) - - [`constructor(parent, props)`](#constructorparent-props) - - [`setup()`](#setup) - - [`willStart()`](#willstart) - - [`mounted()`](#mounted) - - [`willUpdateProps(nextProps)`](#willupdatepropsnextprops) - - [`willPatch()`](#willpatch) - - [`patched(snapshot)`](#patchedsnapshot) - - [`willUnmount()`](#willunmount) - - [`catchError(error)`](#catcherrorerror) - - [Root Component](#root-component) - - [Composition](#composition) - - [Form Input Bindings](#form-input-bindings) - - [References](#references) - - [Dynamic sub components](#dynamic-sub-components) - - [Functional Components](#functional-components) - - [SVG Components](#svg-components) +- [Properties and methods](#properties-and-methods) +- [Static Properties](#static-properties) +- [Lifecycle](#lifecycle) + - [`setup`](#setup) + - [`willStart`](#willstart) + - [`willRender`](#willrender) + - [`rendered`](#rendered) + - [`mounted`](#mounted) + - [`willUpdateProps`](#willupdateprops) + - [`willPatch`](#willpatch) + - [`patched`](#patched) + - [`willUnmount`](#willunmount) + - [`willDestroy`](#willdestroy) + - [`onError`](#onerror) +- [Sub components](#sub-components) +- [Dynamic Sub components](#dynamic-sub-components) +- [`status` helper] ## Overview -OWL components are the building blocks for user interface. They are designed to be: +An Owl component is a small class which represents some part of the user interface. +It is part of a component tree, and has an [environment](environment.md) (`env`), +which is propagated from a parent to its children. -1. **declarative:** the user interface should be described in terms of the state - of the application, not as a sequence of imperative steps. - -2. **composable:** each component can seamlessly be created in a parent component by - a simple tag or directive in its template. - -3. **asynchronous rendering:** the framework will transparently wait for each - sub components to be ready before applying the rendering. It uses native promises - under the hood. - -4. **uses QWeb as a template system:** the templates are described in XML - and follow the QWeb specification. This is a requirement for Odoo. - -OWL components are defined as a subclass of Component. The rendering is -exclusively done by a [QWeb](qweb_templating_language.md) template (which needs to be preloaded in QWeb). -Rendering a component generates a virtual dom representation -of the component, which is then patched to the DOM, in order to apply the changes in an efficient way. - -## Example - -Let us have a look at a simple component: +OWL components are defined by subclassing the `Component` class. For example, +here is how a `Counter` component could be implemented: ```javascript -const { useState } = owl.hooks; - -class ClickCounter extends owl.Component { - state = useState({ value: 0 }); - - increment() { - this.state.value++; - } -} -``` - -```xml - -``` - -Note that this code is written in ESNext style, so it will only run on the -latest browsers without a transpilation step. - -This example shows how a component should be defined: it simply subclasses the -Component class. If no static `template` key is defined, then -Owl will use the component's name as template name. Here, -a state object is defined, by using the `useState` hook. It is not mandatory to use the state object, but it is certainly encouraged. The result of the `useState` call is -[observed](observer.md), and any change to it will cause a rerendering. - -## Reference - -An Owl component is a small class which represents a component or some UI element. -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_templating_language.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 -not define a `template` key, then Owl will lookup in QWeb to -find a template with the component name (or one of its ancestors). - -### Reactive system - -OWL components are normal javascript classes. So, changing a component internal -state does nothing more: - -```js -class Counter extends Component { - static template = xml`
`; - state = { value: 0 }; - - increment() { - this.state.value++; - } -} -``` - -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 -explicit call to `render` in `increment`: - -```js - increment() { - this.state.value++; - this.render(); - } -``` - -However, it may be simple in this case, but it quickly become cumbersome, as a -component get more complex, and its internal state is modified by more than one -method. - -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 -changes. The `useState` hook generates a proxy version of an object -(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: - -```js -const { useState } = owl.hooks; - -class Counter extends Component { - static template = xml`
`; - state = useState({ value: 0 }); - - increment() { - this.state.value++; - } -} -``` - -Obviously, we can call the `useState` hook more than once: - -```js -const { useState } = owl.hooks; +const { Component, xml, useState } = owl; class Counter extends Component { static template = xml` -
- - -
`; - counter1 = useState({ value: 0 }); - counter2 = useState({ value: 0 }); + `; - increment(counter) { - counter.value++; + state = useState({ value: 0 }); + + increment() { + this.state.value++; } } ``` -Note that hooks are subject to one important [rule](hooks.md#one-rule): they need -to be called in the constructor. +In this example, we use the `xml` helper to define inline templates, and the +`useState` hook, which returns a reactive version of its argument (see the page +on reactivity). -### Properties +## Properties and methods -- **`el`** (HTMLElement | null): reference to the DOM root node of the element. It is `null` when the - component is not mounted. +The `Component` class has a very small API. -- **`env`** (Object): the component [environment](environment.md), which contains a QWeb instance. +- **`env (object)`**: the component [environment](environment.md) -- **`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 component gives a `user` and a `color` value to the `ChildComponent`. - - ```xml -
- -
- ``` +- **`props (object)`**: this is an object containing all the [props](props.md) given by + the parent to a child component Note that `props` are owned by the parent, not by the component. As such, it should not ever be modified by the component (otherwise you risk @@ -189,14 +68,20 @@ to be called in the constructor. component will go through the following lifecycle methods: `willUpdateProps`, `willPatch` and `patched`. -### Static Properties +* **`render()`**: calling this method directly will cause a rerender. Note + that with the reactivity system, this should be rare to have to do it manually. + Also, the rendering operation is asynchronous, so the DOM will only be updated + slightly later (at the next animation frame, if no component delays the + rendering) -- **`template`** (string, optional): if given, this is the name of the QWeb template that will render the component. Note that there is a helper `xml` to +## Static Properties + +- **`template (string)`**: this is the name of the template that + will render the component. Note that there is a helper `xml` to make it easy to define an inline template. -* **`components`** (Object, optional): if given, this is an object that contains - the classes of any sub components needed by the template. This is the main way - used by Owl to be able to create sub components. +* **`components (object, optional)`**: if given, this is an object that contains + the classes of any sub components needed by the template. ```js class ParentComponent extends owl.Component { @@ -204,10 +89,10 @@ to be called in the constructor. } ``` -* **`props`** (Object, optional): if given, this is an object that describes the +* **`props (object, optional)`**: if given, this is an object that describes the type and shape of the (actual) props given to the component. If Owl mode is `dev`, this will be used to validate the props each time the component is - created/updated. See [Props Validation](props_validation.md) for more information. + created/updated. See [Props Validation](props.md#props-validation) for more information. ```js class Counter extends owl.Component { @@ -218,10 +103,11 @@ to be called in the constructor. } ``` -- **`defaultProps`** (Object, optional): if given, this object define default +- **`defaultProps (object, optional)`**: if given, this object define default values for (top-level) props. Whenever `props` are given to the object, they will be altered to add default value (if missing). Note that it does not - change the initial object, a new object will be created instead. + change the initial object, a new object will be created instead. See + [default props](props.md#default-props) for more information ```js class Counter extends owl.Component { @@ -231,153 +117,32 @@ to be called in the constructor. } ``` -- **`style`** (string, optional): it should be the return value of the [`css` tag](tags.md#css-tag), - which is used to inject stylesheet whenever the component is visible on the - screen. +## Lifecycle -There is another static property defined on the `Component` class: `current`. -This property is set to the currently being defined component (in the constructor). -This is the way [hooks](hooks.md) are able to get a reference to the target -component. - -### Methods - -We explain here all the public methods of the `Component` class. - -- **`mount(target, options)`** (async): this is the main way a - component is added to the DOM: the root component is mounted to a target - HTMLElement (or document fragment). Obviously, this is asynchronous, since each children need to be - created as well. Most applications will need to call `mount` exactly once, on - the root component. - - The `options` argument is an optional object with a `position` key. The - `position` key can have three possible values: `first-child`, `last-child`, `self`. - - - `first-child`: with this option, the component will be prepended inside the target, - - `last-child` (default value): with this option, the component will be - appended in the target element, - - `self`: the target will be used as the root element for the component. This - means that the target has to be an HTMLElement (and not a document fragment). - In this situation, it is possible that the component cannot be unmounted. For - example, if its target is `document.body`. - - Note that if a component is mounted, unmounted and remounted, it will be - automatically re-rendered to ensure that changes in its state (or something - in the environment) will be taken into account. - - If a component is mounted inside an element or a fragment which is not in the - DOM, then it will be rendered fully, but not active: the `mounted` hooks will - not be called. This is sometimes useful if we want to load an application in - memory. In that case, we need to mount the root component again in an element - which is in the DOM: - - ```js - const app = new App(); - await app.mount(document.createDocumentFragment()); - // app is rendered in memory, but not active - await app.mount(document.body); - // app is now visible - ``` - - Note that the normal way of mounting an application is by using the `mount` - method on a component class, not by creating the instance by hand. See the - documentation on [mounting applications](mounting.md). - -* **`unmount()`**: in case a component needs to be detached/removed from the DOM, this - method can be used. Most applications should not call `unmount`, this is more - useful to the underlying component system. - -* **`render()`** (async): calling this method directly will cause a rerender. Note - that this should be very rare to have to do it manually, the Owl framework is - most of the time responsible for doing that at an appropriate moment. - - Note that the render method is asynchronous, so one cannot observe the updated - DOM in the same stack frame. - -* **`shouldUpdate(nextProps)`**: this method is called each time a component's props - are updated. It returns a boolean, which indicates if the component should - ignore a props update. If it returns false, then `willUpdateProps` will not - be called, and no rendering will occur. Its default implementation is to - always return true. Note that this is an optimization, similar to React's `shouldComponentUpdate`. Most of the time, this should not be used, but it - can be useful if we are handling large number of components. Since this is an - optimization, Owl has the freedom to ignore the result of `shouldUpdate` in - some cases (for example, if a component is remounted, or if we want to force - a full rerender of the UI). However, if `shouldUpdate` returns true, then Owl - provides the guarantee that the component will be rendered at some point in - the future (except if the component is destroyed or if some part of the UI crashes). - -* **`destroy()`**. As its name suggests, this method will remove the component, - and perform all necessary cleanup, such as unmounting the component, its children, - removing the parent/children relationship. This method should almost never be - called directly (except maybe on the root component), but should be done by the - framework instead. - -Obviously, these methods are reserved for Owl, and should not be used by Owl -users, unless they want to override them. Also, Owl reserves all method names -starting with `__`, in order to prevent possible future conflicts with user code -whenever Owl needs to change. - -### Lifecycle - -A solid and robust component system needs useful hooks/methods to help +A solid and robust component system needs a complete lifecycle system to help developers write components. Here is a complete description of the lifecycle of -a owl component: +a Owl component: -| Method | Description | -| ------------------------------------------------ | ----------------------------------------------------------- | -| **[setup](#setup)** | setup | -| **[willStart](#willstart)** | async, before first rendering | -| **[mounted](#mounted)** | just after component is rendered and added to the DOM | -| **[willUpdateProps](#willupdatepropsnextprops)** | async, before props update | -| **[willPatch](#willpatch)** | just before the DOM is patched | -| **[patched](#patchedsnapshot)** | just after the DOM is patched | -| **[willUnmount](#willunmount)** | just before removing component from DOM | -| **[catchError](#catcherrorerror)** | catch errors (see [error handling page](error_handling.md)) | +| Method | Hook | Description | +| --------------------------------------- | ------------------- | ---------------------------------------------------------------------- | +| **[setup](#setup)** | none | setup | +| **[willStart](#willstart)** | `onWillStart` | async, before first rendering | +| **[willRender](#willrender)** | `onWillRender` | just before component is rendered | +| **[rendered](#rendered)** | `onRendered` | just after component is rendered | +| **[mounted](#mounted)** | `onMounted` | just after component is rendered and added to the DOM | +| **[willUpdateProps](#willupdateprops)** | `onWillUpdateProps` | async, before props update | +| **[willPatch](#willpatch)** | `onWillPatch` | just before the DOM is patched | +| **[patched](#patched)** | `onPatched` | just after the DOM is patched | +| **[willUnmount](#willunmount)** | `onWillUnmount` | just before removing component from DOM | +| **[willDestroy](#willdestroy)** | `onWillDestroy` | just before component is destroyed | +| **[error](#onerror)** | `onError` | catch and handle errors (see [error handling page](error_handling.md)) | -Notes: - -- hooks call order is precisely defined: `[willX]` hooks are called first on parent, - then on children, and `[Xed]` are called in the reverse order: first children, - then parent. -- no hook method should ever be called manually. They are supposed to be - called by the owl framework whenever it is required. - -#### `constructor(parent, props)` - -The constructor is not exactly a hook, it is the regular, -normal, constructor of the component. Since it is not a hook, you need to make -sure that `super` is called. - -This is usually where you would set the initial state and the template of the -component. - -```javascript - constructor(parent, props) { - super(parent, props); - this.state = useState({someValue: true}); - this.template = 'mytemplate'; - } -``` - -Note that with ESNext class fields, the constructor method does not need to be -implemented in most cases: - -```javascript -class ClickCounter extends owl.Component { - state = useState({ value: 0 }); - - ... -} -``` - -Hook functions can be called in the constructor. - -#### `setup()` +### `setup` _setup_ is run just after the component is constructed. It is a lifecycle method, very similar to the _constructor_, except that it does not receive any argument. -It is a valid method to call hook functions. Note that one of the main reason to +It is the proper place to call hook functions. Note that one of the main reason to have the `setup` hook in the component lifecycle is to make it possible to monkey patch it. It is a common need in the Odoo ecosystem. @@ -387,59 +152,122 @@ setup() { } ``` -#### `willStart()` +### `willStart` -willStart is an asynchronous hook that can be implemented to -perform some action before the initial rendering of a component. +`willStart` is an asynchronous hook that can be implemented to +perform some (most of the time asynchronous) action before the initial rendering of a component. It will be called exactly once before the initial rendering. It is useful in some cases, for example, to load external assets (such as a JS library) before the component is rendered. Another use case is to load data from a server. +The `onWillStart` hook is used to register a function that will be executed at +this moment: + ```javascript - async willStart() { - await owl.utils.loadJS("my-awesome-lib.js"); + setup() { + onWillStart(async () => { + this.data = await this.loadData() + }); } ``` -At this point, the component is not yet rendered. Note that a slow `willStart` method will slow down the rendering of the user -interface. Therefore, some care should be made to make this method as -fast as possible. +At this point, the component is not yet rendered. Note that slow `willStart` +code will slow down the rendering of the user interface. Therefore, some care +should be made to make this method as fast as possible. -#### `mounted()` +Note that if there are more than one `onWillStart` registered callback, then they +will all be run in parallel. -`mounted` is called each time a component is attached to the -DOM, after the initial rendering and possibly later if the component was unmounted -and remounted. At this point, the component is considered _active_. This is a good place to add some listeners, or to interact with the +### `willRender` + +It is uncommon but it may happen that one need to execute code just before a +component is rendered (more precisely, when its compiled template function is executed). +To do that, one can use the `onWillRender` hook: + +```javascript + setup() { + onWillRender(() => { + // do something + }); + } +``` + +`willRender` hooks are called just before rendering templates, parent first, +then children. + +### `rendered` + +It is uncommon but it may happen that one need to execute code just after a +component is rendered (more precisely, when its compiled template function is executed). +To do that, one can use the `onRendered` hook: + +```javascript + setup() { + onRendered(() => { + // do something + }); + } +``` + +`rendered` hooks are called just after rendering templates, parent first, +then children. Note that at this moment, the actual DOM may not exist yet (if +it is the first rendering), or is not updated yet. This will be dom in the next +animation frame as soon as all the components are ready. + +### `mounted` + +The `mounted` hook is called each time a component is attached to the +DOM, after the initial rendering. At this point, the component is considered +_active_. This is a good place to add some listeners, or to interact with the DOM, if the component needs to perform some measure for example. It is the opposite of `willUnmount`. If a component has been mounted, it will always be unmounted at some point in the future. The mounted method will be called recursively on each of its children. First, -the parent, then all its children. +children, then parents. It is allowed (but not encouraged) to modify the state in the `mounted` hook. Doing so will cause a rerender, which will not be perceptible by the user, but will slightly slow down the component. -#### `willUpdateProps(nextProps)` +The `onMounted` hook is used to register a function that will be executed at +this moment: -The willUpdateProps is an asynchronous hook, called just before new props +```javascript + setup() { + onMounted(() => { + // do something here + }); + } +``` + +### `willUpdateProps` + +The `willUpdateProps` is an asynchronous hook, called just before new props are set. This is useful if the component needs to perform an asynchronous task, depending on the props (for example, assuming that the props are some record Id, fetching the record data). +The `onWillUpdateProps` hook is used to register a function that will be executed at +this moment: + ```javascript - willUpdateProps(nextProps) { - return this.loadData({id: nextProps.id}); + setup() { + onWillUpdateProps(nextProps => { + return this.loadData({id: nextProps.id}); + }); } ``` -This hook is not called during the first render (but willStart is called -and performs a similar job). +Notice that it receives the next props for the component. -#### `willPatch()` +This hook is not called during the first render (but `willStart` is called +and performs a similar job). Also, as most of the hooks, it is called in the +usual order: parents first, then children. + +### `willPatch` The willPatch hook is called just before the DOM patching process starts. It is not called on the initial render. This is useful to read @@ -450,7 +278,20 @@ Note that modifying the state 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 DOM state. Also, it will not be called if the component is not in the DOM. -#### `patched(snapshot)` +The `onWillPatch` hook is used to register a function that will be executed at +this moment: + +```javascript + setup() { + onWillPatch(() => { + this.scrollState = this.getScrollSTate(); + }); + } +``` + +The `willPatch` is called in the usual parent->children order. + +### `patched` This hook is called whenever a component did actually update its DOM (most likely via a change in its state/props or environment). @@ -460,312 +301,131 @@ with the DOM (for example, through an external library) whenever the component was patched. Note that this hook will not be called if the component is not in the DOM. +The `onPatched` hook is used to register a function that will be executed at +this moment: + +```javascript + setup() { + onPatched(() => { + this.scrollState = this.getScrollSTate(); + }); + } +``` + Updating the component state in this hook is possible, but not encouraged. 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()` +Like `mounted`, the `patched` hook is called in the order: children first, then +parent. -willUnmount is a hook that is called each time just before a component is unmounted from -the DOM. This is a good place to remove listeners, for example. +### `willUnmount` + +`willUnmount` is a hook that is called each time just before a component is +unmounted from the DOM. This is a good place to remove listeners, for example. + +The `onWillUnmount` hook is used to register a function that will be executed at +this moment: ```javascript - mounted() { - this.env.bus.on('someevent', this, this.doSomething); - } - willUnmount() { - this.env.bus.off('someevent', this, this.doSomething); + setup() { + onMounted(() => { + // add some listener + }); + onWillUnmount(() => { + // remove listener + }); } ``` -This is the opposite method of `mounted`. +This is the opposite method of `mounted`. Note that if a component is destroyed +before being mounted, the `willUnmount` method may not be called. -#### `catchError(error)` +Parent `willUnmount` hooks will be called before children. -The `catchError` method is useful when we need to intercept and properly react -to (rendering) errors that occur in some sub components. See the page on -[error handling](error_handling.md). +### `willDestroy` -### Root Component +Sometimes, components need to do some action in the `setup` and clean it up when +they are inactive. However, the `willUnmount` hook is not appropriate for the +cleaning operation, since the component may be destroyed before it has even been +mounted. The `willDestroy` hook is useful in that situation, since it is always +called. -Most of the time, an Owl component will be created automatically by a tag (or the `t-component` -directive) in a template. There is however an obvious exception: the root component -of an Owl application has to be created manually: +The `onWillUnmount` hook is used to register a function that will be executed at +this moment: -```js -class App extends owl.Component { ... } - -const app = new App(); -app.mount(document.body); +```javascript + setup() { + onWillDestroy(() => { + // do some cleanup + }); + } ``` -The root component does not have a parent nor `props` (see note below). It will be setup with an -[environment](environment.md) (either the `env` defined on its class, or a -default empty environment). +The `willDestroy` hooks are first called on children, then on parents. -Note: a root component can however be given a `props` object in its constructor, -like this: `new App(null, {some: 'object'});`. It will not be a true `props` -object, managed by Owl (so, for example, it will never be updated). +### `onError` -### Composition +Sadly, it may happen that components crashes at runtime. This is an unfortunate +reality, and this is why Owl needs to provide a way to handle these errors. -The example above shows a QWeb template with a sub component. In a template, -components are declared with a tagname corresponding to the class name. It has -to be capitalized. +The `onError` hook is useful when we need to intercept and properly react +to errors that occur in some sub components. See the page on +[error handling](error_handling.md) for more detail. -```xml -
- some text - -
+```javascript + setup() { + onError(() => { + // do something + }); + } ``` +## Sub components + +It is convenient to define a component using other (sub) components. This is +called composition, and is very powerful in practice. To do that in Owl, one +can just use a tag starting with a capital letter in its template, and register +the sub component class in its static `components` object: + ```js -class ParentComponent extends owl.Component { - static components = { MyComponent: MyComponent}; - ... +class Child extends Component { + static template = xml`
child component
`; +} + +class Parent extends Component { + static template = xml` +
+ + +
`; + + static components = { Child }; } ``` -In this example, the `ParentComponent`'s template creates a component `MyComponent` just -after the span. The `info` key will be added to the subcomponent's `props`. Each -`props` is a string which represents a javascript (QWeb) expression, so it is -dynamic. If it is necessary to give a string, this can be done by quoting it: -`someString="'somevalue'"`. - -Note that the rendering context for the template is the component itself. This means -that the template can access `state` (if it exists), `props`, `env`, or any -methods defined in the component. - -```xml -
- -
-``` - -```js -class ParentComponent { - static components = { ChildComponent }; - state = useState({ val: 4 }); -} -``` - -Whenever the template is rendered, it will automatically create the subcomponent -`ChildComponent` at the correct place. It needs to find the reference to the -actual component class in the special static `components` key, or the class registered in -QWeb's global registry (see `register` function of QWeb). It first looks inside -the static `components` key, then fallbacks on the global registry. - -_Props_: In this example, the child component will receive the object `{count: 4}` in its -constructor. This will be assigned to the `props` variable, which can be accessed -on the component (and also, in the template). Whenever the state is updated, then -the sub component will also be updated automatically. See the [props section](props.md) +This example also shows how one can pass information from the parent component +to the child component, as props. See the [props section](props.md) for more information. -**CSS and style:** Owl allows the parent to declare -additional css classes or style for the sub component: css declared in `class`, `style`, `t-att-class` or `t-att-style` will be added to the -root component element. - -```xml -
- -
-``` - -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 -of the possible classes. Otherwise, it will not be able to make the difference -between a valid css class added by the component, or other custom code, and a -class that need to be removed. This is why we only support the explicit syntax -with a class object: - -```xml - -``` - -### Form Input Bindings - -It is very common to need to be able to read the value out of an html `input` (or -`textarea`, or `select`) in order to use it (note: it does not need to be in a -form!). A possible way to do this is to do it by hand: - -```js -class Form extends owl.Component { - state = useState({ text: "" }); - - _updateInputValue(event) { - this.state.text = event.target.value; - } -} -``` - -```xml -
- - -
-``` - -This works. However, this requires a little bit of _plumbing_ code. Also, the -plumbing code is slightly different if you need to interact with a checkbox, -or with radio buttons, or with select tags. - -To help with this situation, Owl has a builtin directive `t-model`: its value -should be an observed value in the component (usually `state.someValue`). With -the `t-model` directive, we can write a shorter code, equivalent to the previous -example: - -```js -class Form extends owl.Component { - state = { text: "" }; -} -``` - -```xml -
- - -
-``` - -The `t-model` directive works with ``, ``, -``, `