From cb07c99d4018dc05a7942ab34291498800904757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 27 Oct 2020 14:41:09 +0100 Subject: [PATCH] [IMP] owl: add a new mount method --- README.md | 5 +-- doc/learning/how_to_test.md | 3 +- doc/learning/quick_start.md | 19 ++++----- doc/learning/tutorial_todoapp.md | 21 ++++------ doc/readme.md | 1 + doc/reference/component.md | 4 ++ doc/reference/content.md | 9 ++-- doc/reference/environment.md | 10 ++--- doc/reference/misc.md | 5 +-- doc/reference/mounting.md | 60 ++++++++++++++++++++++++++ doc/reference/utils.md | 8 ++-- src/component/component.ts | 31 ++++++++++++++ src/index.ts | 3 +- tests/component/component.test.ts | 39 +++++++++-------- tests/component/un_mounting.test.ts | 26 ++++-------- tools/playground/app.js | 8 ++-- tools/playground/samples.js | 65 ++++++++++++----------------- 17 files changed, 191 insertions(+), 126 deletions(-) create mode 100644 doc/reference/mounting.md diff --git a/README.md b/README.md index 8f86f711..c061da06 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ find some more additional information [here](doc/miscellaneous/comparison.md). Here is a short example to illustrate interactive components: ```javascript -const { Component, useState } = owl; +const { Component, useState, mount } = owl; const { xml } = owl.tags; class Counter extends Component { @@ -63,8 +63,7 @@ class App extends Component { static components = { Counter }; } -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); ``` Note that the counter component is made reactive with the [`useState` hook](doc/reference/hooks.md#usestate). diff --git a/doc/learning/how_to_test.md b/doc/learning/how_to_test.md index b8fb4901..ca1fd70c 100644 --- a/doc/learning/how_to_test.md +++ b/doc/learning/how_to_test.md @@ -85,8 +85,7 @@ afterEach(() => { describe("SomeComponent", () => { test("component behaves as expected", async () => { const props = {...}; // depends on the component - const comp = new SomeComponent(null, props); - await comp.mount(fixture); + const comp = await mount(SomeComponent, { target: fixture, props }); // do some assertions expect(...).toBe(...); diff --git a/doc/learning/quick_start.md b/doc/learning/quick_start.md index e4f99246..48deae64 100644 --- a/doc/learning/quick_start.md +++ b/doc/learning/quick_start.md @@ -54,7 +54,7 @@ Now, `index.html` should contain the following: And `app.js` should look like this: ```js -const { Component } = owl; +const { Component, mount } = owl; const { xml } = owl.tags; const { whenReady } = owl.utils; @@ -65,8 +65,7 @@ class App extends Component { // Setup code function setup() { - const app = new App(); - app.mount(document.body); + mount(App, target: { document.body }) } whenReady(setup); @@ -124,7 +123,7 @@ Here is the content of `app.js` and `main.js`: ```js // app.js ---------------------------------------------------------------------- -const { Component } = owl; +const { Component, mount } = owl; const { xml } = owl.tags; export class App extends Component { @@ -135,8 +134,7 @@ export class App extends Component { import { App } from "./app.js"; function setup() { - const app = new App(); - app.mount(document.body); + mount(App, { target: document.body }); } owl.utils.whenReady(setup); @@ -240,12 +238,11 @@ export class App extends Component { } // src/main.js ----------------------------------------------------------------- -import { utils } from "@odoo/owl"; +import { utils, mount } from "@odoo/owl"; import { App } from "./components/App"; function setup() { - const app = new App(); - app.mount(document.body); + mount(App, { target: document.body }); } utils.whenReady(setup); @@ -253,6 +250,7 @@ utils.whenReady(setup); // tests/components/App.test.js ------------------------------------------------ import { App } from "../../src/components/App"; import { makeTestFixture, nextTick, click } from "../helpers"; +import { mount } from "@odoo/owl"; let fixture; @@ -266,8 +264,7 @@ afterEach(() => { describe("App", () => { test("Works as expected...", async () => { - const app = new App(); - await app.mount(fixture); + await mount(App, { target: fixture }); expect(fixture.innerHTML).toBe("
Hello Owl
"); click(fixture, "div"); diff --git a/doc/learning/tutorial_todoapp.md b/doc/learning/tutorial_todoapp.md index 90b507cf..aec1f8c9 100644 --- a/doc/learning/tutorial_todoapp.md +++ b/doc/learning/tutorial_todoapp.md @@ -83,7 +83,7 @@ a single root component. Let us start by defining an `App` component. Replace th content of the function in `app.js` by the following code: ```js -const { Component } = owl; +const { Component, mount } = owl; const { xml } = owl.tags; const { whenReady } = owl.utils; @@ -94,8 +94,7 @@ class App extends Component { // Setup code function setup() { - const app = new App(); - app.mount(document.body); + mount(App, { target: document.body }); } whenReady(setup); @@ -279,8 +278,7 @@ class App extends Component { // ------------------------------------------------------------------------- function setup() { owl.config.mode = "dev"; - const app = new App(); - app.mount(document.body); + mount(App, { target: document.body }); } whenReady(setup); @@ -639,8 +637,7 @@ function setup() { owl.config.mode = "dev"; const store = new Store({ actions, state: initialState }); App.env.store = store; - const app = new App(); - app.mount(document.body); + mount(App, { target: document.body }); } whenReady(setup); @@ -666,9 +663,8 @@ function makeStore() { function setup() { owl.config.mode = "dev"; - App.env.store = makeStore(); - const app = new App(); - app.mount(document.body); + const env = {store = makeStore()}; + mount(App, { target: document.body, env }); } ``` @@ -943,9 +939,8 @@ For reference, here is the final code: function setup() { owl.config.mode = "dev"; - App.env.store = makeStore(); - const app = new App(); - app.mount(document.body); + const env = {store = makeStore()}; + mount(App, { target: document.body, env }); } whenReady(setup); diff --git a/doc/readme.md b/doc/readme.md index c0e7db0c..4507ac14 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -27,6 +27,7 @@ provided by Owl. - [Event Handling](reference/event_handling.md) - [Error Handling](reference/error_handling.md) - [Hooks](reference/hooks.md) +- [Mounting a component](reference/mounting.md) - [Miscellaneous Components](reference/misc.md) - [Observer](reference/observer.md) - [Props](reference/props.md) diff --git a/doc/reference/component.md b/doc/reference/component.md index a9136f79..9510c6b4 100644 --- a/doc/reference/component.md +++ b/doc/reference/component.md @@ -270,6 +270,10 @@ We explain here all the public methods of the `Component` class. // 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. diff --git a/doc/reference/content.md b/doc/reference/content.md index 5eba3793..54d74508 100644 --- a/doc/reference/content.md +++ b/doc/reference/content.md @@ -10,10 +10,11 @@ exported as `owl.core.EventBus`. Component misc Context AsyncRoot QWeb Portal -Store router -useState Link -config RouteComponent - mode Router +mount router +Store Link +useState RouteComponent +config Router + mode core tags EventBus css Observer xml diff --git a/doc/reference/environment.md b/doc/reference/environment.md index 90bbee0b..21f62b7d 100644 --- a/doc/reference/environment.md +++ b/doc/reference/environment.md @@ -49,15 +49,14 @@ The correct way to customize an environment is to simply set it up on the root component class, before the first component is created: ```js -App.env = { +const env = { _t: myTranslateFunction, user: {...}, services: { ... }, }; -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body, env }); ``` It is also possible to simply share an environment between all root components, @@ -121,9 +120,8 @@ async function myEnv() { } async function start() { - App.env = await myEnv(); - const app = new App(); - await app.mount(document.body); + const env = await myEnv(); + mount(App, { target: document.body, env }); } ``` diff --git a/doc/reference/misc.md b/doc/reference/misc.md index 27b1a474..06ccfee0 100644 --- a/doc/reference/misc.md +++ b/doc/reference/misc.md @@ -43,7 +43,7 @@ workflow to help the user put in some data, which it could use later on. JavaScript: ```js -const { Component } = owl; +const { Component, mount } = owl; const { Portal } = owl.misc; class TeleportedComponent extends Component {} @@ -51,8 +51,7 @@ class App extends Component { static components = { Portal, TeleportedComponent }; } -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); ``` XML: diff --git a/doc/reference/mounting.md b/doc/reference/mounting.md new file mode 100644 index 00000000..f1e57a1d --- /dev/null +++ b/doc/reference/mounting.md @@ -0,0 +1,60 @@ +# 🦉 Mounting an application 🦉 + +## Content + +- [Overview](#overview) +- [API](#api) + +## Overview + +Mounting an Owl application is done by using the `mount` method (available in +`owl.mount` if you are using the iife build, or it can be directly imported +from `owl` if you are using a module system): + +```js +const mount = { owl }; // if owl is available as an object + +const env = { ... }; +const app = await mount(MyComponent, { target: document.body, env }); +``` + +Another example: + +```js +const config = { + env: ..., + props: ..., + target: document.body, + position: "self", +}; +const app = await mount(App, config); +``` + +A common way to initialize an application is to first setup an environment, +then to call the `mount` method. + +## API + +Mount takes two parameters: + +- `C`, which should be a component class (NOT instance), +- `params`, which is an object with the following keys: + - `target (HTMLElement | DocumentFragment)`: the target of the mount operation + - `env (optional, Env)` an environment + - `position (optional, "first-child" | "last-child" | "self")` the position + where it should be mounted (see below for more informations) + - `props (optional, any)`: some initial values that are given as props. Useful + when the root component is configurable, or when testing sub components + +Here are the various positions supported by Owl: + +- `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`. + +The `mount` method returns a promise that resolves to the instance of the created +component. diff --git a/doc/reference/utils.md b/doc/reference/utils.md index 61668e87..5213bd05 100644 --- a/doc/reference/utils.md +++ b/doc/reference/utils.md @@ -21,8 +21,8 @@ argument, it executes it as soon as the DOM ready (or directly). ```js Promise.all([loadFile("templates.xml"), owl.utils.whenReady()]).then(function ([templates]) { const qweb = new owl.QWeb({ templates }); - const app = new App({ qweb }); - app.mount(document.body); + const env = { qweb }; + await mount(App, { env, target: document.body }); }); ``` @@ -31,8 +31,8 @@ or alternatively: ```js owl.utils.whenReady(function () { const qweb = new owl.QWeb(); - const app = new App({ qweb }); - app.mount(document.body); + const env = { qweb }; + await mount(App, { env, target: document.body }); }); ``` diff --git a/src/component/component.ts b/src/component/component.ts index 17f000dc..faafa16f 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -729,3 +729,34 @@ export class Component { } } } + +interface MountParameters { + env?: Env; + target: HTMLElement | DocumentFragment; + props?: any; + position?: MountOptions["position"]; +} + +interface Type extends Function { + new (...args: any[]): T; +} + +export async function mount>( + C: T, + params: MountParameters +): Promise> { + const { env, props, target } = params; + let origEnv = C.hasOwnProperty("env") ? (C as any).env : null; + if (env) { + ((C as any) as typeof Component).env = env; + } + const component: Component = new C(null, props); + if (origEnv) { + (C as any).env = origEnv; + } else { + delete (C as any).env; + } + const position = params.position || "last-child"; + await component.mount(target, { position }); + return component as any; +} diff --git a/src/index.ts b/src/index.ts index df54acae..f4d6ff1a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,7 @@ import { Link } from "./router/link"; import { RouteComponent } from "./router/route_component"; import { Router } from "./router/router"; -export { Component } from "./component/component"; +export { Component, mount } from "./component/component"; export { QWeb }; export { config }; @@ -37,4 +37,5 @@ export const hooks = Object.assign({}, _hooks, { useGetters: _store.useGetters, useStore: _store.useStore, }); + export const __info__ = {}; diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts index d4eec141..6d17f08c 100644 --- a/tests/component/component.test.ts +++ b/tests/component/component.test.ts @@ -1,4 +1,4 @@ -import { Component, Env } from "../../src/component/component"; +import { Component, Env, mount } from "../../src/component/component"; import { EventBus } from "../../src/core/event_bus"; import { useRef, useState } from "../../src/hooks"; import { QWeb } from "../../src/qweb/qweb"; @@ -69,18 +69,23 @@ describe("basic widget properties", () => { class SomeWidget extends Component { static template = xml`
content
`; } - const widget = new SomeWidget(); - widget.mount(fixture); - await nextTick(); + await mount(SomeWidget, { target: fixture }); expect(fixture.innerHTML).toBe("
content
"); }); + test("can be mounted with props", async () => { + class SomeWidget extends Component { + static template = xml`
`; + } + await mount(SomeWidget, { target: fixture, props: { content: "foo" } }); + expect(fixture.innerHTML).toBe("
foo
"); + }); + test("can be mounted on a documentFragment", async () => { class SomeWidget extends Component { static template = xml`
content
`; } - const widget = new SomeWidget(); - await widget.mount(document.createDocumentFragment()); + const widget = await mount(SomeWidget, { target: document.createDocumentFragment() }); expect(fixture.innerHTML).toBe(""); await widget.mount(fixture); expect(fixture.innerHTML).toBe("
content
"); @@ -90,10 +95,9 @@ describe("basic widget properties", () => { class SomeWidget extends Component { static template = xml`
content
`; } - const widget = new SomeWidget(); let error; try { - await widget.mount(null as any); + await mount(SomeWidget, { target: null as any }); } catch (e) { error = e; } @@ -107,10 +111,9 @@ describe("basic widget properties", () => { class SomeWidget extends Component { static template = xml``; } - const widget = new SomeWidget(); let error; try { - await widget.mount(fixture); + await mount(SomeWidget, { target: fixture }); } catch (e) { error = e; } @@ -137,8 +140,7 @@ describe("basic widget properties", () => { }); } - const counter = new Counter(); - counter.mount(fixture); + const counter = await mount(Counter, { target: fixture }); await nextTick(); expect(fixture.innerHTML).toBe("
0
"); const button = (counter.el).getElementsByTagName("button")[0]; @@ -156,8 +158,7 @@ describe("basic widget properties", () => { static components = { Child }; } - const parent = new Parent(); - await parent.mount(fixture); + await mount(Parent, { target: fixture }); expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot(); expect(fixture.innerHTML).toBe("
"); }); @@ -171,9 +172,8 @@ describe("basic widget properties", () => { }); } - const counter = new Counter(); const target = document.createElement("div"); - await counter.mount(target); + const counter = await mount(Counter, { target: target }); expect(target.innerHTML).toBe("
0
"); const button = (counter.el).getElementsByTagName("button")[0]; button.click(); @@ -188,8 +188,7 @@ describe("basic widget properties", () => {
world
`; } - const widget = new StyledWidget(); - await widget.mount(fixture); + await mount(StyledWidget, { target: fixture }); expect(fixture.innerHTML).toBe(`
world
`); }); @@ -212,8 +211,8 @@ describe("basic widget properties", () => { steps.push("patched"); } } - const widget = new TestW(); - await widget.mount(fixture); + await mount(TestW, { target: fixture }); + expect(steps).toEqual(["__render", "mounted"]); }); diff --git a/tests/component/un_mounting.test.ts b/tests/component/un_mounting.test.ts index 10411a1e..553989ec 100644 --- a/tests/component/un_mounting.test.ts +++ b/tests/component/un_mounting.test.ts @@ -1,4 +1,4 @@ -import { Component, Env } from "../../src/component/component"; +import { Component, Env, mount } from "../../src/component/component"; import { useState } from "../../src/hooks"; import { xml } from "../../src/tags"; import { makeDeferred, makeTestEnv, makeTestFixture, nextTick, nextMicroTick } from "../helpers"; @@ -37,8 +37,7 @@ describe("mount targets", () => { div.innerHTML = `

pre-existing

`; fixture.appendChild(div); - const app = new App(); - await app.mount(div, { position: "self" }); + const app = await mount(App, { target: div, position: "self" }); expect(fixture.innerHTML).toBe( `

pre-existing

app

another tag

` @@ -66,10 +65,9 @@ describe("mount targets", () => { const div = document.createElement("div"); fixture.appendChild(div); - const app = new App(); let error; try { - await app.mount(div, { position: "self" }); + await mount(App, { target: div, position: "self" }); } catch (e) { error = e; } @@ -84,8 +82,7 @@ describe("mount targets", () => { const span = document.createElement("span"); fixture.appendChild(span); - const app = new App(); - await app.mount(fixture, { position: "first-child" }); + await mount(App, { target: fixture, position: "first-child" }); expect(fixture.innerHTML).toBe("
app
"); }); @@ -96,8 +93,7 @@ describe("mount targets", () => { const span = document.createElement("span"); fixture.appendChild(span); - const app = new App(); - await app.mount(fixture, { position: "last-child" }); + await mount(App, { target: fixture, position: "last-child" }); expect(fixture.innerHTML).toBe("
app
"); }); @@ -108,8 +104,7 @@ describe("mount targets", () => { const span = document.createElement("span"); fixture.appendChild(span); - const app = new App(); - await app.mount(fixture); + await mount(App, { target: fixture }); expect(fixture.innerHTML).toBe("
app
"); }); }); @@ -133,8 +128,7 @@ describe("unmounting and remounting", () => { } } - const w = new MyWidget(); - await w.mount(fixture); + const w = await mount(MyWidget, { target: fixture }); expect(fixture.innerHTML).toBe("
Hey
"); expect(steps).toEqual(["willstart", "mounted"]); @@ -162,8 +156,7 @@ describe("unmounting and remounting", () => { } } - const w = new MyWidget(); - await w.mount(fixture); + const w = await mount(MyWidget, { target: fixture }); await w.mount(fixture); expect(fixture.innerHTML).toBe("
Hey
"); expect(steps).toEqual(["willstart", "mounted"]); @@ -203,8 +196,7 @@ describe("unmounting and remounting", () => { state = useState({ val: 1, flag: true }); } - const widget = new Parent(); - await widget.mount(fixture); + const widget = await mount(Parent, { target: fixture }); expect(steps).toEqual(["render"]); expect(fixture.innerHTML).toBe("
12
"); widget.state.flag = false; diff --git a/tools/playground/app.js b/tools/playground/app.js index 3345f67a..79b60377 100644 --- a/tools/playground/app.js +++ b/tools/playground/app.js @@ -1,5 +1,6 @@ import { SAMPLES } from "./samples.js"; -const { useState, useRef, onMounted, onWillUnmount } = owl.hooks; +const { mount, hooks } = owl; +const { useState, useRef, onMounted, onWillUnmount } = hooks; //------------------------------------------------------------------------------ // Constants, helpers, utils //------------------------------------------------------------------------------ @@ -419,9 +420,8 @@ async function start() { owl.utils.whenReady() ]); const qweb = new owl.QWeb({ templates }); - owl.Component.env = { qweb }; - const app = new App(); - app.mount(document.body); + const env = { qweb }; + await mount(App, {target: document.body, env}); } start(); diff --git a/tools/playground/samples.js b/tools/playground/samples.js index 0423f140..b4cda13d 100644 --- a/tools/playground/samples.js +++ b/tools/playground/samples.js @@ -1,5 +1,5 @@ const COMPONENTS = `// In this example, we show how components can be defined and created. -const { Component, useState } = owl; +const { Component, useState, mount } = owl; class Greeter extends Component { constructor() { @@ -22,8 +22,7 @@ class App extends Component { App.components = { Greeter }; // Application setup -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); `; const COMPONENTS_XML = ` @@ -50,7 +49,7 @@ const COMPONENTS_CSS = `.greeter { const ANIMATION = `// The goal of this component is to see how the t-transition directive can be // used to generate simple transition effects. -const { Component, useState } = owl; +const { Component, useState, mount } = owl; class Counter extends Component { constructor() { @@ -80,8 +79,7 @@ class App extends Component { } App.components = { Counter }; -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); `; const ANIMATION_XML = ` @@ -193,7 +191,7 @@ const LIFECYCLE_DEMO = `// This example shows all the possible lifecycle hooks // methods in the console. Try modifying its state by clicking on it, or by // clicking on the two main buttons, and look into the console to see what // happens. -const { Component, useState } = owl; +const { Component, useState, mount } = owl; class DemoComponent extends Component { constructor() { @@ -240,8 +238,7 @@ class App extends Component { } App.components = { DemoComponent }; -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); `; const LIFECYCLE_DEMO_XML = ` @@ -273,7 +270,8 @@ const LIFECYCLE_CSS = `button { }`; const HOOKS_DEMO = `// In this example, we show how hooks can be used or defined. -const {useState, onMounted, onWillUnmount} = owl.hooks; +const { hooks, mount } = owl; +const {useState, onMounted, onWillUnmount} = hooks; // We define here a custom behaviour: this hook tracks the state of the mouse // position @@ -312,8 +310,7 @@ class App extends owl.Component { } // Application setup -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); `; const HOOKS_DEMO_XML = ` @@ -332,7 +329,7 @@ const HOOKS_CSS = `button { const CONTEXT_JS = `// In this example, we show how components can use the Context and 'useContext' // hook to share information between them. -const { Component, Context } = owl; +const { Component, Context, mount } = owl; const { useContext } = owl.hooks; class ToolbarButton extends Component { @@ -367,8 +364,7 @@ const themeContext = new Context({ }); // Add the themeContext the environment to make it available to all components App.env.themeContext = themeContext; -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); `; const CONTEXT_XML = ` @@ -395,7 +391,7 @@ const TODO_APP_STORE = `// This example is an implementation of the TodoList app // // In this implementation, we use the owl Store class to manage the state. It // is very similar to the VueX store. -const { Component, useState } = owl; +const { Component, useState, mount } = owl; const { useRef, useStore, useDispatch, onPatched, onMounted } = owl.hooks; //------------------------------------------------------------------------------ @@ -568,8 +564,7 @@ function makeStore() { } TodoApp.env.store = makeStore(); -const app = new TodoApp(); -app.mount(document.body); +mount(TodoApp, { target: document.body }); `; const TODO_APP_STORE_XML = ` @@ -1060,8 +1055,7 @@ function setupResponsivePlugin(env) { //------------------------------------------------------------------------------ setupResponsivePlugin(App.env); -const app = new App(); -app.mount(document.body); +owl.mount(App, { target: document.body }); `; const RESPONSIVE_XML = ` @@ -1173,7 +1167,7 @@ const SLOTS = `// We show here how slots can be used to create generic component // // Note that the t-on-click event, defined in the App template, is executed in // the context of the App component, even though it is inside the Card component -const { Component, useState } = owl; +const { Component, useState, mount } = owl; class Card extends Component { constructor() { @@ -1211,8 +1205,8 @@ class App extends Component { App.components = {Card, Counter}; // Application setup -const app = new App(); -app.mount(document.body);`; +mount(App, { target: document.body }); +`; const SLOTS_XML = `
@@ -1298,7 +1292,7 @@ const ASYNC_COMPONENTS = `// This example will not work if your browser does not // However, we don't want renderings of the other sub component to be delayed // because of the slow component. We use the AsyncRoot component for this // purpose. Try removing it to see the difference. -const { Component, useState } = owl; +const { Component, useState, mount } = owl; const { AsyncRoot } = owl.misc; class SlowComponent extends Component { @@ -1329,8 +1323,7 @@ class App extends Component { } App.components = {SlowComponent, NotificationList, AsyncRoot}; -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); `; const ASYNC_COMPONENTS_XML = ` @@ -1385,7 +1378,7 @@ const FORM = `// This example illustrate how the t-model directive can be used t // 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. -const { Component, useState } = owl; +const { Component, useState, mount } = owl; class Form extends Component { constructor() { @@ -1401,8 +1394,7 @@ class Form extends Component { } // Application setup -const form = new Form(); -form.mount(document.body); +mount(Form, { target: document.body }); `; const FORM_XML = ` @@ -1448,7 +1440,7 @@ const PORTAL_COMPONENTS = ` // This shows the expected use case of Portal // which is to implement something similar // to bootstrap modal -const { Component, useState } = owl; +const { Component, useState, mount } = owl; const { Portal } = owl.misc; class Modal extends Component {} @@ -1470,8 +1462,7 @@ class App extends Component { App.components = { Dialog , Interstellar }; // Application setup -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); `; const PORTAL_XML = ` @@ -1559,7 +1550,7 @@ const WMS = `// This example is slightly more complex than usual. We demonstrate // - minimal width/height // - better heuristic for initial window position // - ... -const { Component, useState } = owl; +const { Component, useState, mount } = owl; const { useRef } = owl.hooks; class HelloWorld extends Component {} @@ -1699,8 +1690,7 @@ const windows = [ ]; App.env.windows = windows; -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); `; const WMS_XML = ` @@ -1818,7 +1808,7 @@ const SFC = `// This example illustrates how Owl enables single file components, // Note that this example has no external xml or css file, everything is // contained in a single js file. -const { Component, useState, tags } = owl; +const { Component, useState, tags, mount } = owl; const { xml, css } = tags; // Counter component @@ -1850,8 +1840,7 @@ App.template = APP_TEMPLATE; App.components = { Counter }; // Application setup -const app = new App(); -app.mount(document.body); +mount(App, { target: document.body }); `; export const SAMPLES = [