diff --git a/CHANGELOG.md b/CHANGELOG.md index cfef44018..ccfb3281a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,7 @@ All changes are documented here in no particular order. - finer grained reactivity: owl 2 tracks change per key/component - finer grained reactivity: sub components can reobserve state ([doc](doc/reference/reactivity.md)) -- new: `reactive` function: create reactive state (without being linked to a component) ([doc](doc/reference/reactivity.md#reactive)) +- new: `reactive` function: create reactive state (without being linked to a component) ([doc](doc/reference/reactivity.md#proxy)) - new: `markRaw` function: mark an object or array so that it is ignored by the reactivity system ([doc](doc/reference/reactivity.md#markraw)) - new: `toRaw` function: given a reactive objet, return the raw (non reactive) underlying object ([doc](doc/reference/reactivity.md#toraw)) @@ -308,7 +308,7 @@ prepended in something, maybe a `div`. Remember that you the root component can have multiple roots Documentation: -- [Fragments](doc/reference/templates.md#fragments) +- [Fragments](doc/reference/template_syntax.md#fragments) - [Mounting a component](doc/reference/app.md#mount-helper) @@ -522,7 +522,7 @@ us so we can study this usecase. In Owl, a call to `useSubEnv` would define a new environment for the children AND the component. It is very useful, but in some cases, one only need to update the children component environment. This can now be done with a new hook: -[`useChildSubEnv`](doc/reference/hooks.md#usesubenv-and-usechildsubenv) +[`useChildSubEnv`](doc/reference/hooks.md) ### 28. `env` is now frozen @@ -593,7 +593,7 @@ So, the following template works for components: hello ``` -Documentation: [Fragments](doc/reference/templates.md#fragments) +Documentation: [Fragments](doc/reference/template_syntax.md#fragments) ### 32. `renderToString` on QWeb has been removed @@ -731,7 +731,7 @@ Otherwise, it will be escaped (just like `t-esc`). Migration: replace all `t-raw` uses by `t-out`, and uses the `markup` function to mark all the js values. -Documentation: [Outputting data](doc/reference/templates.md#outputting-data) +Documentation: [Outputting data](doc/reference/template_syntax.md#outputting-data) ## 39. `browser` object has been removed diff --git a/README.md b/README.md index 0c9b78296..68d918204 100644 --- a/README.md +++ b/README.md @@ -81,51 +81,41 @@ This example demonstrates Owl's reactivity: `todos` is a signal, `remaining` is a computed value that updates automatically, and the UI reacts to changes without manual subscription management. -## Design Principles +## Documentation -Owl is built on principles that make it powerful yet approachable: - -**Explicit over Implicit** - -Reactiveness is explicit — you read signals by calling them (`this.count()`), -making dependencies visible and bugs easier to trace. No hidden magic. - -**Composable Architecture** - -Plugins provide a structured way to share state and services across components. -They compose naturally and support full type inference. - -**Scales with You** - -Start simple with inline templates and signals. Grow into a large codebase -with external templates, registries, and plugins. Owl powers Odoo's -multi-million-line codebase — it's proven at scale. - -**Developer Experience** - -First-class TypeScript support, comprehensive error messages in dev mode, -and a browser devtools extension for debugging. - -## Resources +The documentation below is for **Owl 3**. For the Owl 2 documentation, see the +[owl-2.x branch](https://github.com/odoo/owl/tree/owl-2.x). ### Getting Started - **[Playground](https://odoo.github.io/owl/playground)** — Interactive examples and live coding -- **[Owl 3.x Release Notes](release_notes.md)** — Complete guide to all changes - [Tutorial: Getting Started](https://odoo.github.io/owl/playground#getting_started) — Learn Owl fundamentals step by step - [Tutorial: Todo List](https://odoo.github.io/owl/playground#todo_list) — Build a full TodoMVC app - [Tutorial: Hibou OS](https://odoo.github.io/owl/playground#hibou_os) — Build a desktop-like interface -### Reference Documentation - -- [Overview](doc/readme.md) -- [App](doc/reference/app.md) | [Component](doc/reference/component.md) -- [Reactivity](doc/reference/reactivity.md) | [Hooks](doc/reference/hooks.md) -- [Templates](doc/reference/templates.md) | [Props](doc/reference/props.md) -- [Slots](doc/reference/slots.md) - -### Understanding Owl - +### Reference + +- [API Reference](doc/readme.md) — A complete list of everything exported by the Owl library +- [App](doc/reference/app.md) — Configure and mount an Owl application to the DOM +- [Component](doc/reference/component.md) — Define components with lifecycle methods and static properties +- [Error Handling](doc/reference/error_handling.md) — Catch and recover from errors in components +- [Event Handling](doc/reference/event_handling.md) — Handle DOM events with t-on directives +- [Form Bindings](doc/reference/form_bindings.md) — Bind form inputs to reactive state with t-model +- [Hooks](doc/reference/hooks.md) — Use lifecycle hooks and other built-in hooks in components +- [Plugins](doc/reference/plugins.md) — Share state and services across components with type-safe plugins +- [Props](doc/reference/props.md) — Pass data to child components with validation and defaults +- [Reactivity](doc/reference/reactivity.md) — Manage state with signals, computed values, and reactive objects +- [Refs](doc/reference/refs.md) — Access DOM elements from components with t-ref +- [Resources and Registries](doc/reference/resources_and_registries.md) — Ordered reactive collections for shared data +- [Slots](doc/reference/slots.md) — Compose components with named and dynamic slot content +- [Template Syntax](doc/reference/template_syntax.md) — Write XML templates with QWeb directives +- [Translations](doc/reference/translations.md) — Translate templates and dynamic strings +- [Types Validation](doc/reference/types_validation.md) — Validate data structures at runtime with a declarative schema + +### Misc + +- [Owl 3.x Release Notes](release_notes.md) — Complete guide to all changes in Owl 3 +- [Design Principles](doc/miscellaneous/design_principles.md) - [Why we built Owl](doc/miscellaneous/why_owl.md) - [Architecture Notes](doc/miscellaneous/architecture.md) - [Comparison with React/Vue](doc/miscellaneous/comparison.md) diff --git a/doc/miscellaneous/comparison.md b/doc/miscellaneous/comparison.md index 4b8b7eb31..e4f5be8ab 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/templates.md#inline-templates) tag helper function, which makes use of +example is the [`xml`](../reference/template_syntax.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/templates.md#inline-templates) tag helper: +[`xml`](../reference/template_syntax.md#inline-templates) tag helper: ```js class Clock extends Component { @@ -142,13 +142,12 @@ class Clock extends Component { ## Asynchronous Rendering This is actually a big difference between OWL and React/Vue: components in OWL -are totally asynchronous. They have two asynchronous hooks in their lifecycle: +are totally asynchronous. They have an asynchronous hook in their lifecycle: - `willStart` (before the component starts rendering) -- `willUpdateProps` (before new props are set) -Both these methods can be implemented and return a promise. The rendering will -then wait for these promises to be completed before patching the DOM. This is +This method can be implemented and return a promise. The rendering will +then wait for the promise to be completed before patching the DOM. This is useful for some use cases: for example, a component may want to fetch an external library (a calendar component may need a specialized calendar rendering library), in its willStart hook. diff --git a/doc/miscellaneous/design_principles.md b/doc/miscellaneous/design_principles.md new file mode 100644 index 000000000..7d79fc0c1 --- /dev/null +++ b/doc/miscellaneous/design_principles.md @@ -0,0 +1,24 @@ +# Design Principles + +Owl is built on principles that make it powerful yet approachable: + +**Explicit over Implicit** + +Reactiveness is explicit — you read signals by calling them (`this.count()`), +making dependencies visible and bugs easier to trace. No hidden magic. + +**Composable Architecture** + +Plugins provide a structured way to share state and services across components. +They compose naturally and support full type inference. + +**Scales with You** + +Start simple with inline templates and signals. Grow into a large codebase +with external templates, registries, and plugins. Owl powers Odoo's +multi-million-line codebase — it's proven at scale. + +**Developer Experience** + +First-class TypeScript support, comprehensive error messages in dev mode, +and a browser devtools extension for debugging. diff --git a/doc/readme.md b/doc/readme.md index 0dcd45d7e..2ab5dd43b 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -1,49 +1,59 @@ -# 🦉 Owl overview 🦉 +# 🦉 API Reference 🦉 -Here is a list of everything exported by the Owl library: +Here is a list of everything exported by the Owl library. -Main entities: +## Core -- [`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 -- [`xml`](reference/templates.md#inline-templates): helper to define an inline template +- [`App`](reference/app.md): an Owl application (root components, templates, and config) +- [`Component`](reference/component.md): base class for Owl components +- [`mount`](reference/app.md#mount-helper): mount a component to a DOM target +- [`xml`](reference/template_syntax.md#inline-templates): define an inline template +- [`props`](reference/props.md): declare and validate component props +- [`status`](reference/component.md#status-helper): get the status of a component (new, mounted, destroyed) -Reactivity +## Reactivity -- [`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) -- [`markRaw`](reference/reactivity.md#markraw): mark an object or array so that it is ignored by the reactivity system -- [`toRaw`](reference/reactivity.md#toraw): given a reactive objet, return the raw (non reactive) underlying object +- [`signal`](reference/reactivity.md#signals): create a reactive value +- [`computed`](reference/reactivity.md#computed-values): create a lazily-evaluated derived value +- [`proxy`](reference/reactivity.md#proxy): create a reactive proxy for an object +- [`effect`](reference/reactivity.md#effects): create a side effect that re-runs when dependencies change +- [`markRaw`](reference/reactivity.md#markraw): mark an object so it is ignored by the reactivity system +- [`toRaw`](reference/reactivity.md#toraw): given a proxy, return the underlying non-reactive object +- [`untrack`](reference/reactivity.md#untrack): execute a function without tracking reactive dependencies -Lifecycle hooks: +## 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 +- [`onWillStart`](reference/component.md#willstart): async, before first rendering +- [`onMounted`](reference/component.md#mounted): after component is rendered and added to the DOM +- [`onWillPatch`](reference/component.md#willpatch): before the DOM is patched +- [`onPatched`](reference/component.md#patched): after the DOM is patched +- [`onWillUnmount`](reference/component.md#willunmount): before removing component from DOM +- [`onWillDestroy`](reference/component.md#willdestroy): before component is destroyed +- [`onError`](reference/component.md#onerror): catch and handle errors -Other hooks: +## 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 -- [`useListener`](reference/hooks.md#uselistener): add a listener outside of a component DOM -- [`useRef`](reference/hooks.md#useref): get an object representing a reference (`t-ref`) -- [`useChildSubEnv`](reference/hooks.md#usesubenv-and-usechildsubenv): extend the current env with additional information (for child components) -- [`useSubEnv`](reference/hooks.md#usesubenv-and-usechildsubenv): extend the current env with additional information (for current component and child components) +- [`useEffect`](reference/hooks.md#useeffect): create a reactive effect, cleaned up on destroy +- [`useListener`](reference/hooks.md#uselistener): add a listener to a target, removed on destroy +- [`useApp`](reference/hooks.md#useapp): get the current App instance -Utility/helpers: +## Plugins + +- [`Plugin`](reference/plugins.md): base class for plugins +- [`plugin`](reference/plugins.md): import a plugin dependency +- [`providePlugins`](reference/plugins.md): make plugins available to a component subtree + +## Type Validation + +- [`types`](reference/types_validation.md#validators): built-in type validators +- [`validateType`](reference/types_validation.md#validatetype): check a value against a type validator +- [`assertType`](reference/types_validation.md#asserttype): like validateType, but throws on failure + +## Utilities - [`EventBus`](reference/utils.md#eventbus): a simple event bus -- [`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) -- [`validate`](reference/utils.md#validate): validates if an object satisfies a specified schema -- [`whenReady`](reference/utils.md#whenready): utility function to execute code when DOM is ready -- [`batched`](reference/utils.md#batched): utility function to batch function calls +- [`markup`](reference/template_syntax.md#outputting-data): mark a string as safe HTML (not escaped by `t-out`) +- [`batched`](reference/utils.md#batched): batch function calls into a single microtask execution +- [`whenReady`](reference/utils.md#whenready): execute code when the DOM is ready +- [`Registry`](reference/resources_and_registries.md#registry): ordered key-value collection with reactivity +- [`Resource`](reference/resources_and_registries.md#resource): ordered set collection with reactivity diff --git a/doc/reference/app.md b/doc/reference/app.md index a6b29b370..76336f5d2 100644 --- a/doc/reference/app.md +++ b/doc/reference/app.md @@ -66,7 +66,7 @@ The `config` object is an object with some of the following keys: - **`warnIfNoStaticProps (boolean, default=false)`**: if true, Owl will log a warning whenever it encounters a component that does not provide a [static props description](props.md#props-validation). - **`customDirectives (object)`**: if given, the corresponding function on the object will be called - on the template custom directives: `t-custom-*` (see [Custom Directives](templates.md#custom-directives)). + on the template custom directives: `t-custom-*` (see [Custom Directives](template_syntax.md#custom-directives)). - **`globalValues (object)`**: Global object of elements available at compilations. ## `mount` helper @@ -149,7 +149,7 @@ const { mount } = owl; Dev mode activates some additional checks and developer amenities: - [Props validation](./props.md#props-validation) is performed -- [t-foreach](./templates.md#loops) loops check for key unicity +- [t-foreach](./template_syntax.md#loops) loops check for key unicity - Lifecycle hooks are wrapped to report their errors in a more developer-friendly way -- onWillStart and onWillUpdateProps will emit a warning in the console when they - take longer than 3 seconds in an effort to ease debugging the presence of deadlocks +- onWillStart will emit a warning in the console when it takes longer than 3 + seconds in an effort to ease debugging the presence of deadlocks diff --git a/doc/reference/component.md b/doc/reference/component.md index 419f69335..a449b29ff 100644 --- a/doc/reference/component.md +++ b/doc/reference/component.md @@ -8,10 +8,7 @@ - [Lifecycle](#lifecycle) - [`setup`](#setup) - [`willStart`](#willstart) - - [`willRender`](#willrender) - - [`rendered`](#rendered) - [`mounted`](#mounted) - - [`willUpdateProps`](#willupdateprops) - [`willPatch`](#willpatch) - [`patched`](#patched) - [`willUnmount`](#willunmount) @@ -24,60 +21,51 @@ ## Overview 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. +It is part of a component tree. OWL components are defined by subclassing the `Component` class. For example, here is how a `Counter` component could be implemented: ```javascript -const { Component, xml, useState } = owl; +const { Component, xml, signal } = owl; class Counter extends Component { static template = xml` - `; - state = useState({ value: 0 }); + count = signal(0); increment() { - this.state.value++; + this.count.set(this.count() + 1); } } ``` -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). +In this example, we use the `xml` helper to define inline templates, and a +`signal` to hold reactive state (see the page on [reactivity](reactivity.md)). ## Properties and methods -The `Component` class has a very small API. +The `Component` base class has a very small API. It does not define any +instance properties by default — it is up to each component to declare what +it needs. -- **`env (object)`**: the component [environment](environment.md) +To access props given by the parent, a component must explicitly import them +using the `props` function. The result can be stored on any property name: -- **`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 - 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 - component will go through the following lifecycle methods: `willUpdateProps`, - `willPatch` and `patched`. +```js +class MyComponent extends Component { + static template = xml``; -* **`render(deep[=false])`**: 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) + myProps = props({ name: t.string }); +} +``` - By default, the render initiated by this method will stop at each child - component if their props are (shallow) equal. To force a render to update - all child components, one can use the optional `deep` argument. Note that the - value of the `deep` argument needs to be a boolean, not a truthy value. +Note that props are owned by the parent, not by the component. As such, they +should not be modified by the component. See the [props page](props.md) for +more information. ## Static Properties @@ -89,58 +77,27 @@ The `Component` class has a very small API. the classes of any sub components needed by the template. ```js - class ParentComponent extends owl.Component { + class ParentComponent extends Component { static components = { SubComponent }; } ``` -* **`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.md#props-validation) for more information. - - ```js - class Counter extends owl.Component { - static props = { - initialValue: Number, - optional: true, - }; - } - ``` - -- **`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. See - [default props](props.md#default-props) for more information - - ```js - class Counter extends owl.Component { - static defaultProps = { - initialValue: 0, - }; - } - ``` - ## Lifecycle 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: -| 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)) | +| Method | Hook | Description | +| ------------------------------- | --------------- | ---------------------------------------------------------------------- | +| **[setup](#setup)** | none | setup | +| **[willStart](#willstart)** | `onWillStart` | async, before first rendering | +| **[mounted](#mounted)** | `onMounted` | just after component is rendered and added to the DOM | +| **[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)) | ### `setup` @@ -184,42 +141,6 @@ should be made to make this method as fast as possible. Note that if there are more than one `onWillStart` registered callback, then they will all be run in parallel. -### `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 @@ -248,30 +169,6 @@ this moment: } ``` -### `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 - setup() { - onWillUpdateProps(nextProps => { - return this.loadData({id: nextProps.id}); - }); - } -``` - -Notice that it receives the next props for the component. - -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. @@ -299,7 +196,7 @@ 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). +likely via a change in its state or props). This method is not called on the initial render. It is useful to interact with the DOM (for example, through an external library) whenever the @@ -396,7 +293,7 @@ the sub component class in its static `components` object: ```js class Child extends Component { - static template = xml`
child component
`; + static template = xml`
child component
`; } class Parent extends Component { @@ -428,9 +325,9 @@ class B extends Component { static template = xml`child b`; } class Parent extends Component { - static template = xml``; + static template = xml``; - state = useState({ child: "a" }); + state = proxy({ child: "a" }); get myComponent() { return this.state.child === "a" ? A : B; diff --git a/doc/reference/concurrency_model.md b/doc/reference/concurrency_model.md index 7c8ba8da0..500bfef86 100644 --- a/doc/reference/concurrency_model.md +++ b/doc/reference/concurrency_model.md @@ -10,8 +10,8 @@ ## Overview Owl was designed from the very beginning with asynchronous components. This comes -from the `willStart` and the `willUpdateProps` lifecycle hooks. With these -asynchronous hooks, it is possible to build complex highly concurrent applications. +from the `willStart` lifecycle hook. With this asynchronous hook, it is possible +to build complex highly concurrent applications. Owl concurrent mode has several benefits: it makes it possible to delay the rendering until some asynchronous operation is complete, it makes it possible @@ -39,10 +39,10 @@ This phase represent the process of rendering a template, in memory, which creat a virtual representation of the desired component html). The output of this phase is a virtual DOM. -It is asynchronous: each subcomponents needs to either be created (so, `willStart` -will need to be called), or updated (which is done with the `willUpdateProps` -method). This is completely a recursive process: a component is the root of a -component tree, and each sub component needs to be (virtually) rendered. +It is asynchronous: each sub component needs to either be created (so, `willStart` +will need to be called), or updated. This is completely a recursive process: a +component is the root of a component tree, and each sub component needs to be +(virtually) rendered. ### Patching @@ -116,8 +116,7 @@ Here is what Owl will do: 1. because of a state change, the method `render` is called on `C` 2. template `C` is rendered again - component `D` is updated: - 1. hook `willUpdateProps` is called on `D` (async) - 2. template `D` is rerendered + 1. template `D` is rerendered - component `F` is created: 1. hook `willStart` is called on `F` (async) 2. template `F` is rendered diff --git a/doc/reference/environment.md b/doc/reference/environment.md index 50069d72e..bb289b624 100644 --- a/doc/reference/environment.md +++ b/doc/reference/environment.md @@ -1,76 +1,5 @@ # 🦉 Environment 🦉 -## Content - -- [Overview](#overview) -- [Setting an Environment](#setting-an-environment) -- [Using a sub environment](#using-a-sub-environment) -- [Content of an Environment](#content-of-an-environment) - -## Overview - -An environment is a shared object given to all components in a tree. It is not -used by Owl itself, but it is useful for application developers to provide a -simple communication channel between components (in addition to the props). - -The `env` given to the [`App`](app.md) is assigned to the `env` component -property. - -``` - Root - / \ - A B -``` - -Also, the `env` object is frozen when the application is started. This is done -to ensure a simpler mental model of what's happening in runtime. Note that it -is only shallowly frozen, so sub objects can be modified. - -## Setting an environment - -The correct way to customize an environment is to simply give it to the `App`, -whenever it is created. - -```js -const env = { - _t: myTranslateFunction, - user: {...}, - services: { - ... - }, -}; - -new App(Root, { env }).mount(document.body); - -// or alternatively -mount(App, document.body, { env }); -``` - -## Using a sub environment - -It is sometimes useful to add one (or more) specific keys to the environment, -from the perspective of a specific component and its children. In that case, the -solution presented above will not work, since it sets the global environment. - -There are two hooks for this situation: [`useSubEnv` and `useChildSubEnv`](hooks.md#usesubenv-and-usechildsubenv). - -```js -class SomeComponent extends Component { - setup() { - useSubEnv({ myKey: someValue }); // myKey is now available for all child components - } -} -``` - -## Content of an Environment - -The `env` object content is totally up to the application developer. However, -some good use cases for additional keys in the environment are: - -- some configuration keys, -- session information, -- generic services (such as doing rpcs). -- other utility functions that one want to inject, such as a translation function. - -Doing it this way means that components are easily testable: we can simply -create a test environment with mock services. +The `env` object from Owl 2 has been removed in Owl 3. Its functionality +(sharing data and services across components) is replaced by the +[plugin system](plugins.md). diff --git a/doc/reference/error_handling.md b/doc/reference/error_handling.md index b51fc51d3..cad9c5ea9 100644 --- a/doc/reference/error_handling.md +++ b/doc/reference/error_handling.md @@ -51,12 +51,13 @@ that render its content, and a fallback if an error happened. ```js class ErrorBoundary extends Component { static template = xml` - An error occurred - `; + An error occurred + `; + + hasError = signal(false); setup() { - this.state = useState({ error: false }); - onError(() => (this.state.error = true)); + onError(() => this.hasError.set(true)); } } ``` @@ -72,4 +73,4 @@ Using the `ErrorBoundary` is then simple simple: Note that we need to be careful here: the fallback UI should not throw any error, otherwise we risk going into an infinite loop (also, see the page on -[slots](slots.md) for more information on the `t-slot` directive). +[slots](slots.md) for more information on slots). diff --git a/doc/reference/event_handling.md b/doc/reference/event_handling.md index ca85ee2b9..cc564f60e 100644 --- a/doc/reference/event_handling.md +++ b/doc/reference/event_handling.md @@ -14,7 +14,7 @@ elements to some specific events. This is what makes a template _alive_. This is done with the `t-on` directive. For example: ```xml - + ``` This will be roughly translated in javascript like this: @@ -30,23 +30,11 @@ can get a reference to the event, or pass some additional arguments. For example all the following expressions are valid: ```xml - + ``` -Notice the use of the `this` keyword in the lambda function: this is the -correct way to call a method on the component in a lambda function. - -One could use the following expression: - -```xml - -``` - -But then, the increment function may be unbound (unless the component binds it -in its setup function, for example). - ## Modifiers In order to remove the DOM event details from the event handlers (like calls to @@ -62,7 +50,7 @@ specified as additional suffixes of the `t-on` directive. | `.synthetic` | define a synthetic event handler (see below) | ```xml - + ``` Note that modifiers can be combined (ex: `t-on-click.stop.prevent`), and that @@ -94,8 +82,8 @@ To enable it, one can just use the `.synthetic` suffix: ```xml
- - @@ -109,7 +97,7 @@ The `t-on` directive also works on a child component: ```xml
in some template - +
``` diff --git a/doc/reference/input_bindings.md b/doc/reference/form_bindings.md similarity index 55% rename from doc/reference/input_bindings.md rename to doc/reference/form_bindings.md index 0fdd10d69..43aba74f4 100644 --- a/doc/reference/input_bindings.md +++ b/doc/reference/form_bindings.md @@ -1,46 +1,43 @@ -# 🦉 Form Input Bindings 🦉 +# 🦉 Form 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: "" }); +class Form extends Component { + static template = xml` +
+ + +
`; + + text = signal(""); - _updateInputValue(event) { - this.state.text = event.target.value; + onInput(event) { + this.text.set(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: +should be a signal (or a computed value). With the `t-model` directive, we can +write a shorter code, equivalent to the previous example: ```js -class Form extends owl.Component { - state = { text: "" }; -} -``` +class Form extends Component { + static template = xml` +
+ + +
`; -```xml -
- - -
+ text = signal(""); +} ``` The `t-model` directive works with ``, ``, @@ -48,11 +45,11 @@ The `t-model` directive works with ``, ``, ```xml
-
Text in an input:
-
Textarea: