mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
+55
-26
@@ -4,12 +4,13 @@
|
|||||||
|
|
||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
- [Example](#example)
|
- [Example](#example)
|
||||||
- [Root Component And Environment](#root-component-and-environment)
|
|
||||||
- [Reference](#reference)
|
- [Reference](#reference)
|
||||||
- [Properties](#properties)
|
- [Properties](#properties)
|
||||||
- [Static Properties](#static-properties)
|
- [Static Properties](#static-properties)
|
||||||
- [Methods](#methods)
|
- [Methods](#methods)
|
||||||
- [Lifecycle](#lifecycle)
|
- [Lifecycle](#lifecycle)
|
||||||
|
- [Root Component](#root-component)
|
||||||
|
- [Environment](#environment)
|
||||||
- [Composition](#composition)
|
- [Composition](#composition)
|
||||||
- [Event Handling](#event-handling)
|
- [Event Handling](#event-handling)
|
||||||
- [Form Input Bindings](#form-input-bindings)
|
- [Form Input Bindings](#form-input-bindings)
|
||||||
@@ -76,30 +77,6 @@ a state object is defined. It is not mandatory to use the state object, but it
|
|||||||
is certainly encouraged. The state object is [observed](observer.md), and any
|
is certainly encouraged. The state object is [observed](observer.md), and any
|
||||||
change to it will cause a rerendering.
|
change to it will cause a rerendering.
|
||||||
|
|
||||||
## Root Component And Environment
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
```js
|
|
||||||
class App extends owl.Component { ... }
|
|
||||||
|
|
||||||
const qweb = new owl.QWeb(TEMPLATES);
|
|
||||||
const env = { qweb: qweb };
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
```
|
|
||||||
|
|
||||||
The root component needs an environment. In Owl, an environment is an object with
|
|
||||||
a `qweb` key, which has to be a [QWeb](qweb.md) instance. This qweb instance will
|
|
||||||
be used to render everything.
|
|
||||||
|
|
||||||
The environment will be given to each child, unchanged, in the `env` property.
|
|
||||||
This can be very useful to share common information/methods. For example, all
|
|
||||||
rpcs can be made through a `rpc` method in the environment. This makes it very
|
|
||||||
easy to test a component.
|
|
||||||
|
|
||||||
## Reference
|
## Reference
|
||||||
|
|
||||||
An Owl component is a small class which represent a component or some UI element.
|
An Owl component is a small class which represent a component or some UI element.
|
||||||
@@ -335,6 +312,59 @@ the DOM. This is a good place to remove some listeners, for example.
|
|||||||
|
|
||||||
This is the opposite method of `mounted`.
|
This is the opposite method of `mounted`.
|
||||||
|
|
||||||
|
### Root Component
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```js
|
||||||
|
class App extends owl.Component { ... }
|
||||||
|
|
||||||
|
const qweb = new owl.QWeb(TEMPLATES);
|
||||||
|
const env = { qweb: qweb };
|
||||||
|
const app = new App(env);
|
||||||
|
app.mount(document.body);
|
||||||
|
```
|
||||||
|
|
||||||
|
The root component needs an environment.
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
|
||||||
|
In Owl, an environment is an object with a `qweb` key, which has to be a
|
||||||
|
[QWeb](qweb.md) instance. This qweb instance will be used to render everything.
|
||||||
|
|
||||||
|
The environment is meant to contain (mostly) static global information and
|
||||||
|
methods for the whole application. For example, settings keys (`mode` to determine
|
||||||
|
if we are in desktop or mobile mode, or `theme`: dark or light), `rpc` methods,
|
||||||
|
session information, ...
|
||||||
|
|
||||||
|
The environment will be given to each child, unchanged, in the `env` property.
|
||||||
|
This can be very useful to share common information/methods. For example, all
|
||||||
|
rpcs can be made through a `rpc` method in the environment. This makes it very
|
||||||
|
easy to test a component.
|
||||||
|
|
||||||
|
Updating the environment is not as simple as changing a component's state: its
|
||||||
|
content is not observed, so updates will not be reflected immediately in the
|
||||||
|
user interface. There is however a mechanism to force root widgets to rerender
|
||||||
|
themselves whenever the environment is modified: one only needs to trigger the
|
||||||
|
`update` event on the QWeb instance. For example, a responsive environment
|
||||||
|
could be programmed like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function setupResponsivePlugin(env) {
|
||||||
|
const isMobile = () => window.innerWidth <= 768;
|
||||||
|
env.isMobile = isMobile();
|
||||||
|
const updateEnv = owl.utils.debounce(() => {
|
||||||
|
if (env.isMobile !== isMobile()) {
|
||||||
|
env.isMobile = !env.isMobile;
|
||||||
|
env.qweb.trigger('update');
|
||||||
|
}
|
||||||
|
}, 15);
|
||||||
|
window.addEventListener("resize", updateEnv);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Composition
|
### Composition
|
||||||
|
|
||||||
The example above shows a QWeb template with a sub component. In a template,
|
The example above shows a QWeb template with a sub component. In a template,
|
||||||
@@ -364,7 +394,6 @@ dynamic. If it is necessary to give a string, this can be done by quoting it:
|
|||||||
Note that the rendering context for the template is the component itself. This means
|
Note that the rendering context for the template is the component itself. This means
|
||||||
that the template can access `state`, `props`, `env`, or any methods defined in the component.
|
that the template can access `state`, `props`, `env`, or any methods defined in the component.
|
||||||
|
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<div t-name="ParentComponent">
|
<div t-name="ParentComponent">
|
||||||
<ChildComponent count="state.val" />
|
<ChildComponent count="state.val" />
|
||||||
|
|||||||
+6
-1
@@ -66,7 +66,7 @@ The component system in Owl requires additional directives, to express various
|
|||||||
needs. Here is a list of all Owl specific directives:
|
needs. Here is a list of all Owl specific directives:
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| -------------------------------------------- | ----------------------------------------------------------------------------------- |
|
| ------------------------------------------- | ----------------------------------------------------------------------------------- |
|
||||||
| `t-component`, `t-keepalive`, `t-asyncroot` | [Defining a sub component](component.md#composition) |
|
| `t-component`, `t-keepalive`, `t-asyncroot` | [Defining a sub component](component.md#composition) |
|
||||||
| `t-ref` | [Setting a reference to a dom node or a sub component](component.md#references) |
|
| `t-ref` | [Setting a reference to a dom node or a sub component](component.md#references) |
|
||||||
| `t-key` | [Defining a key (to help virtual dom reconciliation)](component.md#t-key-directive) |
|
| `t-key` | [Defining a key (to help virtual dom reconciliation)](component.md#t-key-directive) |
|
||||||
@@ -135,6 +135,11 @@ It's API is quite simple:
|
|||||||
qweb.addTemplate("ParentComponent", "<div><Dialog/></div>");
|
qweb.addTemplate("ParentComponent", "<div><Dialog/></div>");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In some way, a `QWeb` instance is the core of an Owl application. It is the only
|
||||||
|
mandatory element of an [environment](component.md#environment). As such, it
|
||||||
|
has an extra responsability: it can act as an event bus for internal communication
|
||||||
|
between Owl classes. This is the reason why `QWeb` actually extends [EventBus](event_bus.md).
|
||||||
|
|
||||||
## Reference
|
## Reference
|
||||||
|
|
||||||
We define in this section the specification of how `QWeb` templates should be
|
We define in this section the specification of how `QWeb` templates should be
|
||||||
|
|||||||
+19
-2
@@ -134,6 +134,19 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
parent.__owl__.children[id] = this;
|
parent.__owl__.children[id] = this;
|
||||||
} else {
|
} else {
|
||||||
this.env = parent;
|
this.env = parent;
|
||||||
|
this.env.qweb.on("update", this, () => {
|
||||||
|
if (this.__owl__.isMounted) {
|
||||||
|
this.render(true);
|
||||||
|
}
|
||||||
|
if (this.__owl__.isDestroyed) {
|
||||||
|
// this is unlikely to happen, but if a root widget is destroyed,
|
||||||
|
// we want to remove our subscription. The usual way to do that
|
||||||
|
// would be to perform some check in the destroy method, but since
|
||||||
|
// it is very performance sensitive, and since this is a rare event,
|
||||||
|
// we simply do it lazily
|
||||||
|
this.env.qweb.off("update", this);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.__owl__ = {
|
this.__owl__ = {
|
||||||
id: id,
|
id: id,
|
||||||
@@ -593,7 +606,9 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
for (let i = 0, l = propsDef.length; i < l; i++) {
|
for (let i = 0, l = propsDef.length; i < l; i++) {
|
||||||
if (!(propsDef[i] in props)) {
|
if (!(propsDef[i] in props)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Missing props '${propsDef[i]}' (component '${this.constructor.name}')`
|
`Missing props '${propsDef[i]}' (component '${
|
||||||
|
this.constructor.name
|
||||||
|
}')`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -603,7 +618,9 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
if (!(propName in props)) {
|
if (!(propName in props)) {
|
||||||
if (propsDef[propName] && !propsDef[propName].optional) {
|
if (propsDef[propName] && !propsDef[propName].optional) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Missing props '${propName}' (component '${this.constructor.name}')`
|
`Missing props '${propName}' (component '${
|
||||||
|
this.constructor.name
|
||||||
|
}')`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|||||||
+6
-4
@@ -1,5 +1,6 @@
|
|||||||
import { VNode, h } from "./vdom";
|
import { VNode, h } from "./vdom";
|
||||||
import { QWebVar, compileExpr } from "./qweb_expressions";
|
import { QWebVar, compileExpr } from "./qweb_expressions";
|
||||||
|
import { EventBus } from "./event_bus";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Owl QWeb Engine
|
* Owl QWeb Engine
|
||||||
@@ -127,7 +128,7 @@ let nextID = 1;
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// QWeb rendering engine
|
// QWeb rendering engine
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
export class QWeb {
|
export class QWeb extends EventBus {
|
||||||
templates: { [name: string]: Template } = {};
|
templates: { [name: string]: Template } = {};
|
||||||
utils = UTILS;
|
utils = UTILS;
|
||||||
static components = Object.create(null);
|
static components = Object.create(null);
|
||||||
@@ -146,6 +147,7 @@ export class QWeb {
|
|||||||
nextSlotId = 1;
|
nextSlotId = 1;
|
||||||
|
|
||||||
constructor(data?: string) {
|
constructor(data?: string) {
|
||||||
|
super();
|
||||||
if (data) {
|
if (data) {
|
||||||
this.addTemplates(data);
|
this.addTemplates(data);
|
||||||
}
|
}
|
||||||
@@ -350,8 +352,8 @@ export class QWeb {
|
|||||||
if (firstLetter === firstLetter.toUpperCase()) {
|
if (firstLetter === firstLetter.toUpperCase()) {
|
||||||
// this is a component, we modify in place the xml document to change
|
// this is a component, we modify in place the xml document to change
|
||||||
// <SomeComponent ... /> to <t t-component="SomeComponent" ... />
|
// <SomeComponent ... /> to <t t-component="SomeComponent" ... />
|
||||||
node.setAttribute('t-component', node.tagName);
|
node.setAttribute("t-component", node.tagName);
|
||||||
node.nodeValue = 't';
|
node.nodeValue = "t";
|
||||||
}
|
}
|
||||||
const attributes = (<Element>node).attributes;
|
const attributes = (<Element>node).attributes;
|
||||||
|
|
||||||
@@ -391,7 +393,7 @@ export class QWeb {
|
|||||||
fullName = name;
|
fullName = name;
|
||||||
value = attributes[j].textContent;
|
value = attributes[j].textContent;
|
||||||
validDirectives.push({ directive, value, fullName });
|
validDirectives.push({ directive, value, fullName });
|
||||||
if (directive.name === "on" || directive.name === 'model') {
|
if (directive.name === "on" || directive.name === "model") {
|
||||||
withHandlers = true;
|
withHandlers = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-24
@@ -1,5 +1,6 @@
|
|||||||
import { Component, Env } from "../src/component";
|
import { Component, Env } from "../src/component";
|
||||||
import { QWeb } from "../src/qweb_core";
|
import { QWeb } from "../src/qweb_core";
|
||||||
|
import { EventBus } from "../src/event_bus";
|
||||||
import {
|
import {
|
||||||
makeDeferred,
|
makeDeferred,
|
||||||
makeTestFixture,
|
makeTestFixture,
|
||||||
@@ -547,10 +548,7 @@ describe("lifecycle hooks", () => {
|
|||||||
|
|
||||||
test("willUpdateProps hook is called", async () => {
|
test("willUpdateProps hook is called", async () => {
|
||||||
let def = makeDeferred();
|
let def = makeDeferred();
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate("Parent", '<span><Child n="state.n"/></span>');
|
||||||
"Parent",
|
|
||||||
'<span><Child n="state.n"/></span>'
|
|
||||||
);
|
|
||||||
class Parent extends Widget {
|
class Parent extends Widget {
|
||||||
state = { n: 1 };
|
state = { n: 1 };
|
||||||
components = { Child: HookWidget };
|
components = { Child: HookWidget };
|
||||||
@@ -599,10 +597,7 @@ describe("lifecycle hooks", () => {
|
|||||||
test("patched hook is called after updateProps", async () => {
|
test("patched hook is called after updateProps", async () => {
|
||||||
let n = 0;
|
let n = 0;
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate("Parent", '<div><Child a="state.a"/></div>');
|
||||||
"Parent",
|
|
||||||
'<div><Child a="state.a"/></div>'
|
|
||||||
);
|
|
||||||
class Parent extends Widget {
|
class Parent extends Widget {
|
||||||
state = { a: 1 };
|
state = { a: 1 };
|
||||||
components = { Child: TestWidget };
|
components = { Child: TestWidget };
|
||||||
@@ -642,10 +637,7 @@ describe("lifecycle hooks", () => {
|
|||||||
|
|
||||||
test("shouldUpdate hook prevent rerendering", async () => {
|
test("shouldUpdate hook prevent rerendering", async () => {
|
||||||
let shouldUpdate = false;
|
let shouldUpdate = false;
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate("Parent", `<div><Child val="state.val"/></div>`);
|
||||||
"Parent",
|
|
||||||
`<div><Child val="state.val"/></div>`
|
|
||||||
);
|
|
||||||
class Parent extends Widget {
|
class Parent extends Widget {
|
||||||
state = { val: 42 };
|
state = { val: 42 };
|
||||||
components = { Child: TestWidget };
|
components = { Child: TestWidget };
|
||||||
@@ -867,7 +859,10 @@ describe("composition", () => {
|
|||||||
|
|
||||||
test("can use components from the global registry", async () => {
|
test("can use components from the global registry", async () => {
|
||||||
QWeb.register("WidgetB", WidgetB);
|
QWeb.register("WidgetB", WidgetB);
|
||||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="WidgetB"/></div>`);
|
env.qweb.addTemplate(
|
||||||
|
"ParentWidget",
|
||||||
|
`<div><t t-component="WidgetB"/></div>`
|
||||||
|
);
|
||||||
class ParentWidget extends Widget {}
|
class ParentWidget extends Widget {}
|
||||||
const widget = new ParentWidget(env);
|
const widget = new ParentWidget(env);
|
||||||
await widget.mount(fixture);
|
await widget.mount(fixture);
|
||||||
@@ -877,7 +872,10 @@ describe("composition", () => {
|
|||||||
|
|
||||||
test("don't fallback to global registry if widget defined locally", async () => {
|
test("don't fallback to global registry if widget defined locally", async () => {
|
||||||
QWeb.register("WidgetB", WidgetB); // should not use this widget
|
QWeb.register("WidgetB", WidgetB); // should not use this widget
|
||||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="WidgetB"/></div>`);
|
env.qweb.addTemplate(
|
||||||
|
"ParentWidget",
|
||||||
|
`<div><t t-component="WidgetB"/></div>`
|
||||||
|
);
|
||||||
env.qweb.addTemplate("AnotherWidgetB", `<span>Belgium</span>`);
|
env.qweb.addTemplate("AnotherWidgetB", `<span>Belgium</span>`);
|
||||||
class AnotherWidgetB extends Widget {}
|
class AnotherWidgetB extends Widget {}
|
||||||
class ParentWidget extends Widget {
|
class ParentWidget extends Widget {
|
||||||
@@ -906,10 +904,7 @@ describe("composition", () => {
|
|||||||
|
|
||||||
test("throw a nice error if it cannot find component", async () => {
|
test("throw a nice error if it cannot find component", async () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate("Parent", `<div><SomeMispelledWidget /></div>`);
|
||||||
"Parent",
|
|
||||||
`<div><SomeMispelledWidget /></div>`
|
|
||||||
);
|
|
||||||
class Parent extends Widget {
|
class Parent extends Widget {
|
||||||
components = { SomeWidget: Widget };
|
components = { SomeWidget: Widget };
|
||||||
}
|
}
|
||||||
@@ -1012,7 +1007,10 @@ describe("composition", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("modifying a sub widget", async () => {
|
test("modifying a sub widget", async () => {
|
||||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="Counter"/></div>`);
|
env.qweb.addTemplate(
|
||||||
|
"ParentWidget",
|
||||||
|
`<div><t t-component="Counter"/></div>`
|
||||||
|
);
|
||||||
class ParentWidget extends Widget {
|
class ParentWidget extends Widget {
|
||||||
components = { Counter };
|
components = { Counter };
|
||||||
}
|
}
|
||||||
@@ -1068,7 +1066,10 @@ describe("composition", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("rerendering a widget with a sub widget", async () => {
|
test("rerendering a widget with a sub widget", async () => {
|
||||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="Counter"/></div>`);
|
env.qweb.addTemplate(
|
||||||
|
"ParentWidget",
|
||||||
|
`<div><t t-component="Counter"/></div>`
|
||||||
|
);
|
||||||
class ParentWidget extends Widget {
|
class ParentWidget extends Widget {
|
||||||
components = { Counter };
|
components = { Counter };
|
||||||
}
|
}
|
||||||
@@ -2721,10 +2722,7 @@ describe("widget and observable state", () => {
|
|||||||
|
|
||||||
test("subcomponents cannot change observable state received from parent", async () => {
|
test("subcomponents cannot change observable state received from parent", async () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate("Parent", `<div><Child obj="state.obj"/></div>`);
|
||||||
"Parent",
|
|
||||||
`<div><Child obj="state.obj"/></div>`
|
|
||||||
);
|
|
||||||
class Parent extends Widget {
|
class Parent extends Widget {
|
||||||
state = { obj: { coffee: 1 } };
|
state = { obj: { coffee: 1 } };
|
||||||
components = { Child };
|
components = { Child };
|
||||||
@@ -3215,3 +3213,38 @@ describe("t-model directive", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("environment and plugins", () => {
|
||||||
|
// some source of external events
|
||||||
|
let bus = new EventBus();
|
||||||
|
|
||||||
|
// definition of a plugin
|
||||||
|
const somePlugin = env => {
|
||||||
|
env.someFlag = true;
|
||||||
|
bus.on("some-event", null, () => {
|
||||||
|
env.someFlag = !env.someFlag;
|
||||||
|
env.qweb.trigger("update");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
test("plugin works as expected", async () => {
|
||||||
|
somePlugin(env);
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="App">
|
||||||
|
<t t-if="env.someFlag">Red</t>
|
||||||
|
<t t-else="1">Blue</t>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class App extends Widget {}
|
||||||
|
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div>Red</div>");
|
||||||
|
bus.trigger("some-event");
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div>Blue</div>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
+15
-16
@@ -942,31 +942,30 @@ class App extends owl.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Application Startup
|
// Responsive plugin
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
function isMobile() {
|
function setupResponsivePlugin(env) {
|
||||||
return window.innerWidth <= 768;
|
const isMobile = () => window.innerWidth <= 768;
|
||||||
|
env.isMobile = isMobile();
|
||||||
|
const updateEnv = owl.utils.debounce(() => {
|
||||||
|
if (env.isMobile !== isMobile()) {
|
||||||
|
env.isMobile = !env.isMobile;
|
||||||
|
env.qweb.trigger('update');
|
||||||
|
}
|
||||||
|
}, 15);
|
||||||
|
window.addEventListener("resize", updateEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Application Startup
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
const env = {
|
const env = {
|
||||||
qweb: new owl.QWeb(TEMPLATES),
|
qweb: new owl.QWeb(TEMPLATES),
|
||||||
isMobile: isMobile()
|
|
||||||
};
|
};
|
||||||
|
setupResponsivePlugin(env);
|
||||||
|
|
||||||
const app = new App(env);
|
const app = new App(env);
|
||||||
app.mount(document.body);
|
app.mount(document.body);
|
||||||
|
|
||||||
function updateEnv() {
|
|
||||||
const _isMobile = isMobile();
|
|
||||||
if (_isMobile !== env.isMobile) {
|
|
||||||
app.updateEnv({
|
|
||||||
isMobile: _isMobile
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("resize", owl.utils.debounce(updateEnv, 20));
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const RESPONSIVE_XML = `<templates>
|
const RESPONSIVE_XML = `<templates>
|
||||||
|
|||||||
Reference in New Issue
Block a user