Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
d3fd269
[IMP] introduce signals and derived values
Goaman Sep 26, 2025
4f3b34d
[ref] update package-lock.json
ged-odoo Nov 20, 2025
af4bf29
[rel] update package.json
ged-odoo Nov 21, 2025
dd9331c
[ADD] add registry and plugin system
ged-odoo Nov 20, 2025
bc6b219
[notes] add release notes
ged-odoo Nov 24, 2025
edac61a
[rel] update package.json to 3.0.0-alpha.2
ged-odoo Nov 24, 2025
e75ad49
[imp] improve typing, fix registry validation code
ged-odoo Nov 24, 2025
a9a622b
[rel] update version to alpha.4
ged-odoo Nov 24, 2025
983b19d
[fix] registry: make it work, add addById method
ged-odoo Nov 24, 2025
76c1fe6
v3.0.0-alpha.5
ged-odoo Nov 24, 2025
9768284
[REM] utils: remove loadFile
ged-odoo Dec 2, 2025
3873dbb
[ref] signals: change their api
ged-odoo Dec 2, 2025
28b9778
[ref] plugins: change api
mcm-odoo Dec 2, 2025
547f9a5
[IMP] plugins: test with components
mcm-odoo Dec 2, 2025
7e5d7ee
[FIX] plugins: fix resource tests
mcm-odoo Dec 2, 2025
f911d07
[ref] utils: rename useExternalListener to useListener
ged-odoo Dec 2, 2025
637d1cd
[ref] rename useState/reactive => proxy
ged-odoo Dec 2, 2025
b048237
[imp] update package.json dev dependencies
ged-odoo Dec 2, 2025
4a5a44d
[imp] streamline app API (Root/subroots => roots)
mcm-odoo Dec 2, 2025
3822372
[fix] various fixes to make the previous commits work
ged-odoo Dec 2, 2025
eb7c5b3
[FIX] playground: fix playground
mcm-odoo Dec 4, 2025
d42c40e
[FIX] revamp error handling code, only throw error once in some cases
ged-odoo Dec 3, 2025
69f0944
[IMP] reactivity: add Signal.update method
ged-odoo Dec 4, 2025
bfc993d
[REF] runtime: move code around to make it clearer
ged-odoo Dec 4, 2025
60e3c37
[REF] reactivity: split code into multiple files
ged-odoo Dec 4, 2025
4064200
[REF] rename withoutReactivity => untrack
ged-odoo Dec 4, 2025
fef3653
[REF] reactivity: move tests
ged-odoo Dec 4, 2025
c88dc5a
[REF] mini cleanup, remove commont/types file
ged-odoo Dec 4, 2025
df49ea8
[IMP] app: remove warnIfNoStaticProps
mcm-odoo Dec 8, 2025
bd1e386
[FIX] app: fix mount return type
mcm-odoo Dec 8, 2025
6092e92
[IMP] props: introduce props function
mcm-odoo Dec 8, 2025
24d5e8e
[IMP] prettify
mcm-odoo Dec 8, 2025
41bd855
[imp] add Resource, make registry chainable
ged-odoo Dec 8, 2025
5730579
[IMP] add Registry.remove, Resource.remove, rename Registry.get => Re…
ged-odoo Dec 8, 2025
bb236df
update snapshots
ged-odoo Dec 8, 2025
ae9b214
[imp] error: simplify error handling
ged-odoo Dec 8, 2025
724c3c9
[imp] simplify plugin and resources system
ged-odoo Dec 9, 2025
465a76b
[add] add some tests for resource
ged-odoo Dec 9, 2025
802b740
[IMP] props: improve props types again
mcm-odoo Dec 9, 2025
7fc0b79
[IMP] hooks: remove env related hooks
mcm-odoo Dec 9, 2025
71efb0d
[IMP] hooks: remove env related hooks
mcm-odoo Dec 9, 2025
939164f
[IMP] props: move default props to props function
mcm-odoo Dec 9, 2025
ff9a28a
[REF] slot: rename t-slot into t-call-slot
mcm-odoo Dec 9, 2025
a906170
[rem] remove onWillRender and onRendered
ged-odoo Dec 9, 2025
0ae959e
[imp] slots: do not get props from component, but from component node
ged-odoo Dec 10, 2025
b793958
[IMP] ref: replace useRef by signal
mcm-odoo Dec 10, 2025
d17818e
[REF] blockdom: simplify ref system
mcm-odoo Dec 11, 2025
8b86701
[IMP] ref: use resource to get multiple refs at once
mcm-odoo Dec 11, 2025
3587755
[IMP] t-model: support signal instead of proxy
mcm-odoo Dec 11, 2025
1837aa9
[IMP] status: status is now a hook
mcm-odoo Dec 11, 2025
33a5ebc
[IMP] plugin: plugin manager now has a status
mcm-odoo Dec 11, 2025
10b329d
[FIX] blockdom: cbRefs used the wrong idx
mcm-odoo Dec 11, 2025
c54004d
[FIX] playground: update playground
mcm-odoo Dec 11, 2025
abc8449
[ref] rework error handling code
ged-odoo Dec 11, 2025
97d0093
[wip] force use of this in rendering context
ged-odoo Dec 9, 2025
cf0740a
[IMP] force use of this in rendering context (mcm pass)
mcm-odoo Dec 12, 2025
23c5842
[REF] t-esc: replace t-esc by t-out
mcm-odoo Dec 12, 2025
40400fe
[IMP] playground: replace t-esc by t-out
mcm-odoo Dec 12, 2025
24207ab
remove t-esc
mcm-odoo Dec 12, 2025
7eba706
remove t-esc tests
mcm-odoo Dec 12, 2025
1b6baa8
[IMP] t-esc: remove t-esc final pass
mcm-odoo Dec 15, 2025
8edc13c
[fix] playground: adapt code
ged-odoo Dec 13, 2025
8cfb7d8
[ref] rename derived => computed
ged-odoo Dec 15, 2025
f38dea8
[add] playground: add list of counters example
ged-odoo Dec 15, 2025
1943b6f
update package.json => v3.0.0-alpha.7
ged-odoo Dec 15, 2025
06283f7
release: alpha.8
ged-odoo Dec 15, 2025
4dfa9f0
[ref] simplify useEffect, fix onWillDestroy
ged-odoo Dec 16, 2025
7c6deb2
v3.0.0-alpha.9
ged-odoo Dec 17, 2025
da75e84
add: .has method on registry/resource
ged-odoo Dec 19, 2025
764a732
add: allow starting an app with plugins list
ged-odoo Dec 22, 2025
169a8cc
make valpha 10
ged-odoo Dec 22, 2025
25e3fda
expose pluginmanager.current
ged-odoo Jan 5, 2026
9d1c01e
fix: computed functions
ged-odoo Jan 6, 2026
8d51f77
v3.0.0-alpha.11
ged-odoo Jan 7, 2026
0d8a7ab
add default id for plugins
ged-odoo Jan 7, 2026
d94777e
allow instantiating a plugin manager without argument
ged-odoo Jan 7, 2026
b61b272
allow using $ in expressions symbols
ged-odoo Jan 7, 2026
4479172
validation: add "extends" key
mcm-odoo Jan 7, 2026
ded85d0
resource & registry constructor options param
mcm-odoo Jan 8, 2026
0b3f5b6
plugin: some changes
mcm-odoo Jan 9, 2026
306dd48
useListener: allow it to be used in plugins, change its meaning
ged-odoo Jan 9, 2026
1360d8a
add: useApp hook
ged-odoo Jan 12, 2026
8d5bda6
signals: change api
mcm-odoo Jan 12, 2026
ee96bf5
update to v3.0.0-alpha.12
ged-odoo Jan 12, 2026
0b4f2cd
plugins: introduce plugin.props function
ged-odoo Jan 13, 2026
389d388
break cyclic dependencies
ged-odoo Jan 13, 2026
c6f41de
new validation api
mcm-odoo Jan 13, 2026
be329f1
wip
ged-odoo Jan 14, 2026
ed25d39
wip
ged-odoo Jan 14, 2026
97c8b57
wip
ged-odoo Jan 14, 2026
8d0a943
wip
ged-odoo Jan 14, 2026
2a8a256
wip
ged-odoo Jan 14, 2026
2278017
wip
ged-odoo Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions doc/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,14 @@ 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
- [`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)

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)
- [`validate`](reference/utils.md#validate): validates if an object satisfies a specified schema
Expand Down
2 changes: 1 addition & 1 deletion doc/reference/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ what it could look like in practice:

```js
// in the main js file:
const { loadFile, mount } = owl;
const { mount } = owl;

// async, so we can use async/await
(async function setup() {
Expand Down
8 changes: 4 additions & 4 deletions doc/reference/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- [`useState`](#usestate)
- [`useRef`](#useref)
- [`useSubEnv` and `useChildSubEnv`](#usesubenv-and-usechildsubenv)
- [`useExternalListener`](#useexternallistener)
- [`useListener`](#uselistener)
- [`useComponent`](#usecomponent)
- [`useEnv`](#useenv)
- [`useEffect`](#useeffect)
Expand Down Expand Up @@ -187,16 +187,16 @@ frozen, to prevent unwanted modifications.
Note that both these hooks can be called an arbitrary number of times. The `env`
will then be updated accordingly.

### `useExternalListener`
### `useListener`

The `useExternalListener` hook helps solve a very common problem: adding and removing
The `useListener` hook helps solve a very common problem: adding and removing
a listener on some target whenever a component is mounted/unmounted. It takes a target
as its first argument, forwards the other arguments to `addEventListener`. For example,
a dropdown menu (or its parent) may need to listen to a `click` event on `window`
to be closed:

```js
useExternalListener(window, "click", this.closeMenu, { capture: true });
useListener(window, "click", this.closeMenu, { capture: true });
```

### `useComponent`
Expand Down
16 changes: 0 additions & 16 deletions doc/reference/utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ functions are all available in the `owl.utils` namespace.
## Content

- [`whenReady`](#whenready): executing code when DOM is ready
- [`loadFile`](#loadfile): loading a file (useful for templates)
- [`EventBus`](#eventbus): a simple EventBus
- [`validate`](#validate): a validation function
- [`batched`](#batched): batch function calls
Expand All @@ -32,21 +31,6 @@ whenReady(function () {
});
```

## `loadFile`

`loadFile` is a helper function to fetch a file. It simply
performs a `GET` request and returns the resulting string in a promise. The
initial usecase for this function is to load a template file. For example:

```js
const { loadFile } = owl;

async function makeEnv() {
const templates = await loadFile("templates.xml");
// do something
}
```

## `EventBus`

It is a simple `EventBus`, with the same API as usual DOM elements, and an
Expand Down
63 changes: 38 additions & 25 deletions docs/playground/playground.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { debounce, loadJS } from "./utils.js";
import {
App,
__info__,
Component,
useState,
useRef,
mount,
onMounted,
onWillUnmount,
onPatched,
onWillUpdateProps,
loadFile as _loadFile,
whenReady,
__info__,
useEffect,
onWillStart,
onWillUnmount,
// onWillUpdateProps,
OwlError,
props,
proxy,
signal,
useEffect,
whenReady,
} from "../owl.js";
import { debounce, loadJS } from "./utils.js";

//------------------------------------------------------------------------------
// Constants, helpers, utils
Expand All @@ -32,7 +33,12 @@ const DEFAULT_XML = `<templates>
const fileCache = {};
const loadFile = (path) => {
if (!(path in fileCache)) {
fileCache[path] = _loadFile(path);
fileCache[path] = fetch(path).then((result) => {
if (!result.ok) {
throw new OwlError("Error while fetching xml templates");
}
return result.text();
});
}
return fileCache[path];
}
Expand Down Expand Up @@ -75,7 +81,12 @@ const SAMPLES = [
code: ["js", "xml", "css"],
},
{
description: "Form Input Bindings",
description: "List of Counters",
folder: "counters",
code: ["js", "xml", "css"],
},
{
description: "t-model",
folder: "form",
code: ["js", "xml"],
},
Expand Down Expand Up @@ -145,23 +156,25 @@ function loadSamples() {
// Tabbed editor
//------------------------------------------------------------------------------
class TabbedEditor extends Component {
props = props();

setup() {
const props = this.props;
this.state = useState({
this.state = proxy({
currentTab: props.js !== false ? "js" : props.xml ? "xml" : "css"
});
this.setTab = debounce(this.setTab.bind(this), 250, true);

this.sessions = {};
this._setupSessions(props);
this.editorNode = useRef("editor");
this.editorNode = signal(null);
this._updateCode = this._updateCode.bind(this);

onMounted(() => {
this.editor = this.editor || ace.edit(this.editorNode.el);
this.editor = this.editor || ace.edit(this.editorNode());

this.editor.setValue(this.props[this.state.currentTab], -1);
this.editor.setFontSize("12px");
this.editor.setFontSize("14px");
this.editor.setTheme("ace/theme/monokai");
this.editor.setSession(this.sessions[this.state.currentTab]);
const tabSize = this.state.currentTab === "xml" ? 2 : 4;
Expand All @@ -185,7 +198,7 @@ class TabbedEditor extends Component {
}
});

onWillUpdateProps((nextProps) => this._setupSessions(nextProps));
// onWillUpdateProps((nextProps) => this._setupSessions(nextProps));

onWillUnmount(() => {
clearInterval(this.interval);
Expand Down Expand Up @@ -252,7 +265,7 @@ class Playground extends Component {
this.version = __info__.version;

this.isDirty = false;
this.state = useState({
this.state = proxy({
js: "",
css: "",
xml: DEFAULT_XML,
Expand Down Expand Up @@ -290,17 +303,17 @@ class Playground extends Component {
this.toggleLayout = debounce(this.toggleLayout, 250, true);
this.runCode = debounce(this.runCode, 250, true);
this.exportStandaloneApp = debounce(this.exportStandaloneApp, 250, true);
this.content = useRef("content");
this.content = signal(null);
this.updateCode = this.updateCode.bind(this);
}

runCode() {
this.content.el.innerHTML = "";
this.content().innerHTML = "";
this.state.displayWelcome = false;

const { js, css, xml } = this.state;
const subiframe = makeCodeIframe(js, css, xml);
this.content.el.appendChild(subiframe);
this.content().appendChild(subiframe);
}

shareCode() {
Expand Down Expand Up @@ -394,10 +407,10 @@ async function start() {
loadFile("templates.xml"),
whenReady()
]);
const rootApp = new App(Playground, { name: "Owl Playground" });
rootApp.addTemplates(templates);

await rootApp.mount(document.body);
await mount(Playground, document.body, {
name: "Owl Playground",
templates,
});
}

start();
4 changes: 2 additions & 2 deletions docs/playground/samples/components/components.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.greeter {
.counter {
font-size: 20px;
width: 300px;
height: 100px;
Expand All @@ -7,4 +7,4 @@
line-height: 100px;
background-color: #eeeeee;
user-select: none;
}
}
23 changes: 8 additions & 15 deletions docs/playground/samples/components/components.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
// In this example, we show how components can be defined and created.
import { Component, useState, mount } from "@odoo/owl";
import { Component, signal, mount } from "@odoo/owl";

class Greeter extends Component {
static template = "Greeter";
class Counter extends Component {
static template = "Counter";

setup() {
this.state = useState({ word: 'Hello' });
}

toggle() {
this.state.word = this.state.word === 'Hi' ? 'Hello' : 'Hi';
count = signal(0);

increment() {
this.count.update(val => val + 1);
}
}

// Main root component
class Root extends Component {
static components = { Greeter };
static components = { Counter };
static template = "Root"

setup() {
this.state = useState({ name: 'World'});
}
}

mount(Root, document.body, { templates: TEMPLATES, dev: true });
7 changes: 4 additions & 3 deletions docs/playground/samples/components/components.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<templates>
<div t-name="Greeter" class="greeter" t-on-click="toggle">
<t t-esc="state.word"/>, <t t-esc="props.name"/>
<div t-name="Counter" class="counter" t-on-click="this.increment">
Count: <t t-out="this.count()"/>
</div>

<t t-name="Root">
<Greeter name="state.name"/>
<Counter/>
<Counter/>
</t>
</templates>
10 changes: 10 additions & 0 deletions docs/playground/samples/counters/counters.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.counter {
font-size: 20px;
width: 300px;
height: 100px;
margin: 5px;
text-align: center;
line-height: 100px;
background-color: #eeeeee;
user-select: none;
}
25 changes: 25 additions & 0 deletions docs/playground/samples/counters/counters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Component, signal, mount, proxy, computed, props } from "@odoo/owl";

class Counter extends Component {
static template = "Counter";

props = props({ count: Function });

increment() {
this.props.count.update((val) => val + 1);
}
}

class Root extends Component {
static components = { Counter };
static template = "Root";

counters = proxy([signal(1), signal(2)]);
sum = computed(() => this.counters.reduce((acc, value) => acc + value(), 0));

addCounter() {
this.counters.push(signal(0));
}
}

mount(Root, document.body, { templates: TEMPLATES, dev: true });
15 changes: 15 additions & 0 deletions docs/playground/samples/counters/counters.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<templates>
<div t-name="Counter" class="counter" t-on-click="this.increment">
Count: <t t-out="this.props.count()"/>
</div>

<t t-name="Root">
<div>
<p>Current sum: <t t-out="this.sum()"/></p>
<button t-on-click="this.addCounter">Add a counter</button>
</div>
<t t-foreach="this.counters" t-as="counter" t-key="counter_index">
<Counter count="counter"/>
</t>
</t>
</templates>
16 changes: 6 additions & 10 deletions docs/playground/samples/form/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@
// data between html inputs (and select/textareas) and the state of a component.
// Note that there are two controls with t-model="color": they are totally
// synchronized.
import { Component, useState, mount } from "@odoo/owl";
import { Component, signal, mount } from "@odoo/owl";

class Form extends Component {
static template = "Form";

setup() {
this.state = useState({
text: "",
othertext: "",
number: 11,
color: "",
bool: false
});
}
text = signal("");
othertext = signal("");
number = signal(11);
color = signal("");
bool = signal(false);
}

// Application setup
Expand Down
Loading