mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f9b6b174e | |||
| 545ceefc3d | |||
| 591508e769 | |||
| d9cbd23cd9 | |||
| f4cd111cc8 | |||
| ea015af741 | |||
| bfc7c81c0d | |||
| e24ff8f8aa | |||
| 9f1e64d399 | |||
| 8e1aa71436 | |||
| be2fc965b1 | |||
| 7245ccf8f9 | |||
| 1c8e1af86d | |||
| 60a6cca960 | |||
| ad55b42ccb | |||
| 50c0a4b126 | |||
| 2ca42e7470 |
@@ -64,8 +64,8 @@ string. More interesting examples can be found on the
|
|||||||
|
|
||||||
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
||||||
|
|
||||||
- [owl-0.16.0.js](https://github.com/odoo/owl/releases/download/v0.16.0/owl.js)
|
- [owl-0.17.0.js](https://github.com/odoo/owl/releases/download/v0.17.0/owl.js)
|
||||||
- [owl-0.16.0.min.js](https://github.com/odoo/owl/releases/download/v0.16.0/owl.min.js)
|
- [owl-0.17.0.min.js](https://github.com/odoo/owl/releases/download/v0.17.0/owl.min.js)
|
||||||
|
|
||||||
Some npm scripts are available:
|
Some npm scripts are available:
|
||||||
|
|
||||||
|
|||||||
+100
-7
@@ -21,6 +21,7 @@
|
|||||||
- [References](#references)
|
- [References](#references)
|
||||||
- [Slots](#slots)
|
- [Slots](#slots)
|
||||||
- [Asynchronous Rendering](#asynchronous-rendering)
|
- [Asynchronous Rendering](#asynchronous-rendering)
|
||||||
|
- [Error Handling](#error-handling)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -116,11 +117,29 @@ find a template with the component name (or one of its ancestor).
|
|||||||
type and shape of the (actual) props given to the component. If Owl mode is
|
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
|
`dev`, this will be used to validate the props each time the component is
|
||||||
created/updated. See [Props Validation](#props-validation) for more information.
|
created/updated. See [Props Validation](#props-validation) for more information.
|
||||||
- **`defaultProps`** (Object, optional): if given, this object define default
|
|
||||||
|
```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
|
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
|
will be altered to add default value (if missing). Note that it does not
|
||||||
change the initial object, a new object will be created instead.
|
change the initial object, a new object will be created instead.
|
||||||
|
|
||||||
|
```js
|
||||||
|
class Counter extends owl.Component {
|
||||||
|
static defaultProps = {
|
||||||
|
initialValue: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
|
||||||
We explain here all the public methods of the `Component` class.
|
We explain here all the public methods of the `Component` class.
|
||||||
@@ -171,7 +190,7 @@ developers write components. Here is a complete description of the lifecycle of
|
|||||||
a owl component:
|
a owl component:
|
||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
| ------------------------------------------------ | ----------------------------------------------------- |
|
| ------------------------------------------------ | ------------------------------------------------------------ |
|
||||||
| **[constructor](#constructorparent-props)** | constructor |
|
| **[constructor](#constructorparent-props)** | constructor |
|
||||||
| **[willStart](#willstart)** | async, before first rendering |
|
| **[willStart](#willstart)** | async, before first rendering |
|
||||||
| **[mounted](#mounted)** | just after component is rendered and added to the DOM |
|
| **[mounted](#mounted)** | just after component is rendered and added to the DOM |
|
||||||
@@ -179,6 +198,7 @@ a owl component:
|
|||||||
| **[willPatch](#willpatch)** | just before the DOM is patched |
|
| **[willPatch](#willpatch)** | just before the DOM is patched |
|
||||||
| **[patched](#patchedsnapshot)** | just after the DOM is patched |
|
| **[patched](#patchedsnapshot)** | just after the DOM is patched |
|
||||||
| **[willUnmount](#willunmount)** | just before removing component from DOM |
|
| **[willUnmount](#willunmount)** | just before removing component from DOM |
|
||||||
|
| **[catchError](#catcherrorerror)** | catch errors (see [error handling section](#error-handling)) |
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
@@ -319,6 +339,12 @@ 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`.
|
||||||
|
|
||||||
|
#### `catchError(error)`
|
||||||
|
|
||||||
|
The `catchError` method is useful when we need to intercept and properly react
|
||||||
|
to (rendering) errors that occur in some sub components. See the section on
|
||||||
|
[error handling](#error-handling)
|
||||||
|
|
||||||
### Root Component
|
### Root Component
|
||||||
|
|
||||||
Most of the time, an Owl component will be created automatically by a tag (or the `t-component`
|
Most of the time, an Owl component will be created automatically by a tag (or the `t-component`
|
||||||
@@ -365,7 +391,7 @@ function setupResponsivePlugin(env) {
|
|||||||
const updateEnv = owl.utils.debounce(() => {
|
const updateEnv = owl.utils.debounce(() => {
|
||||||
if (env.isMobile !== isMobile()) {
|
if (env.isMobile !== isMobile()) {
|
||||||
env.isMobile = !env.isMobile;
|
env.isMobile = !env.isMobile;
|
||||||
env.qweb.trigger('update');
|
env.qweb.trigger("update");
|
||||||
}
|
}
|
||||||
}, 15);
|
}, 15);
|
||||||
window.addEventListener("resize", updateEnv);
|
window.addEventListener("resize", updateEnv);
|
||||||
@@ -786,7 +812,8 @@ of the props. Here is how it works in Owl:
|
|||||||
- props are validated whenever a component is created/updated
|
- props are validated whenever a component is created/updated
|
||||||
- props are only validated in `dev` mode (see [tooling page](tooling.md#development-mode))
|
- props are only validated in `dev` mode (see [tooling page](tooling.md#development-mode))
|
||||||
- if a key does not match the description, an error is thrown
|
- if a key does not match the description, an error is thrown
|
||||||
- it only validates keys defined in (static) `props`. Additional keys in (component) `props` are not validated.
|
- it validates keys defined in (static) `props`. Additional keys given by the
|
||||||
|
parent will cause an error.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@@ -814,15 +841,16 @@ class ComponentB extends owl.Component {
|
|||||||
|
|
||||||
- it is an object or a list of strings
|
- it is an object or a list of strings
|
||||||
- a list of strings is a simplified props definition, which only lists the name
|
- a list of strings is a simplified props definition, which only lists the name
|
||||||
of the props.
|
of the props. Also, if the name ends with `?`, it is considered optional.
|
||||||
- all props are by default required, unless they are defined with `optional: true`
|
- all props are by default required, unless they are defined with `optional: true`
|
||||||
(in that case, validation is only done if there is a value)
|
(in that case, validation is only done if there is a value)
|
||||||
- valid types are: `Number, String, Boolean, Object, Array, Date, Function`, and all
|
- valid types are: `Number, String, Boolean, Object, Array, Date, Function`, and all
|
||||||
constructor functions (so, if you have a `Person` class, it can be used as a type)
|
constructor functions (so, if you have a `Person` class, it can be used as a type)
|
||||||
- arrays are homogeneous (all elements have the same type/shape)
|
- arrays are homogeneous (all elements have the same type/shape)
|
||||||
|
|
||||||
For each key, a `prop` definition is either a constructor, a list of constructors, or an object:
|
For each key, a `prop` definition is either a boolean, a constructor, a list of constructors, or an object:
|
||||||
|
|
||||||
|
- a boolean: indicate that the props exists, and is mandatory.
|
||||||
- a constructor: this should describe the type, for example: `id: Number` describe
|
- a constructor: this should describe the type, for example: `id: Number` describe
|
||||||
the props `id` as a number
|
the props `id` as a number
|
||||||
- a list of constructors. In that case, this means that we allow more than one
|
- a list of constructors. In that case, this means that we allow more than one
|
||||||
@@ -840,6 +868,11 @@ Examples:
|
|||||||
static props = ['message', 'id', 'date'];
|
static props = ['message', 'id', 'date'];
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// size is optional
|
||||||
|
static props = ['message', 'size?'];
|
||||||
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
static props = {
|
static props = {
|
||||||
messageIds: {type: Array, element: Number}, // list of number
|
messageIds: {type: Array, element: Number}, // list of number
|
||||||
@@ -854,7 +887,8 @@ Examples:
|
|||||||
url: String
|
url: String
|
||||||
]}, // object, with keys id (number), name (string, optional) and url (string)
|
]}, // object, with keys id (number), name (string, optional) and url (string)
|
||||||
someFlag: Boolean, // a boolean, mandatory (even if `false`)
|
someFlag: Boolean, // a boolean, mandatory (even if `false`)
|
||||||
someVal: [Boolean, Date] // either a boolean or a date
|
someVal: [Boolean, Date], // either a boolean or a date
|
||||||
|
otherValue: true, // indicates that it is a prop
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -994,3 +1028,62 @@ Here are a few tips on how to work with asynchronous components:
|
|||||||
<AsyncChild t-asyncroot="1"/>
|
<AsyncChild t-asyncroot="1"/>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
By default, whenever an error occurs in the rendering of an Owl application, we
|
||||||
|
destroy the whole application. Otherwise, we cannot offer any guarantee on the
|
||||||
|
state of the resulting component tree. It might be hopelessly corrupted, but
|
||||||
|
without any user-visible state.
|
||||||
|
|
||||||
|
Clearly, it sometimes is a little bit extreme to destroy the application. This
|
||||||
|
is why we have a builtin mechanism to handle rendering errors (and errors coming
|
||||||
|
from lifecycle hooks): the `catchError` hook.
|
||||||
|
|
||||||
|
Whenever the `catchError` lifecycle hook is implemented, all errors coming from
|
||||||
|
sub components rendering and/or lifecycle method calls will be caught and given
|
||||||
|
to the `catchError` method. This allow us to properly handle the error, and to
|
||||||
|
not break the application.
|
||||||
|
|
||||||
|
For example, here is how we could implement an `ErrorBoundary` component:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<div t-name="ErrorBoundary">
|
||||||
|
<t t-if="state.error">
|
||||||
|
Error handled
|
||||||
|
</t>
|
||||||
|
<t t-else="1">
|
||||||
|
<t t-slot="default" />
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
class ErrorBoundary extends Widget {
|
||||||
|
state = { error: false };
|
||||||
|
|
||||||
|
catchError() {
|
||||||
|
this.state.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Using the `ErrorBoundary` is then extremely simple:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ErrorBoundary><SomeOtherComponent/></ErrorBoundary>
|
||||||
|
```
|
||||||
|
|
||||||
|
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, it may be useful to know that whenever an error is caught, it is then
|
||||||
|
broadcasted to the application by an event on the `qweb` instance. It may be
|
||||||
|
useful, for example, to log the error somewhere.
|
||||||
|
|
||||||
|
```js
|
||||||
|
env.qweb.on("error", null, function(error) {
|
||||||
|
// do something
|
||||||
|
// react to the error
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|||||||
@@ -122,6 +122,13 @@ It's API is quite simple:
|
|||||||
const vnode = qweb.render("App", component);
|
const vnode = qweb.render("App", component);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- **`renderToString(name, context)`**: renders a template, but returns an html
|
||||||
|
string.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const str = qweb.renderToString("someTemplate", somecontext);
|
||||||
|
```
|
||||||
|
|
||||||
- **`register(name, Component)`**: static function to register an OWL Component
|
- **`register(name, Component)`**: static function to register an OWL Component
|
||||||
to QWeb's global registry. Globally registered Components can be used in
|
to QWeb's global registry. Globally registered Components can be used in
|
||||||
templates (see the `t-component` directive). This is useful for commonly used
|
templates (see the `t-component` directive). This is useful for commonly used
|
||||||
|
|||||||
+16
-23
@@ -165,17 +165,16 @@ const getters = {
|
|||||||
const post = store.getters.getPost(id);
|
const post = store.getters.getPost(id);
|
||||||
```
|
```
|
||||||
|
|
||||||
Getters take *at most* one argument.
|
Getters take _at most_ one argument.
|
||||||
|
|
||||||
|
|
||||||
Note that getters are cached if they don't take any argument, or their argument
|
Note that getters are cached if they don't take any argument, or their argument
|
||||||
is a string or a number.
|
is a string or a number.
|
||||||
|
|
||||||
### Connecting a Component
|
### Connecting a Component
|
||||||
|
|
||||||
By default, an Owl `Component` is not connected to any store. The `connect`
|
At some point, we need a way to access the state in the store from a component.
|
||||||
function is there to create sub Components that are connected versions of
|
By default, an Owl `Component` is not connected to any store. To do that, we
|
||||||
Components.
|
need to create a component inheriting from `OwlComponent`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const actions = {
|
const actions = {
|
||||||
@@ -193,19 +192,18 @@ const state = {
|
|||||||
};
|
};
|
||||||
const store = new owl.Store({ state, actions, mutations });
|
const store = new owl.Store({ state, actions, mutations });
|
||||||
|
|
||||||
class Counter extends owl.Component {
|
class Counter extends owl.ConnectedComponent {
|
||||||
increment() {
|
static mapStoreToProps(state) {
|
||||||
this.env.store.dispatch("increment");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function mapStoreToProps(state) {
|
|
||||||
return {
|
return {
|
||||||
value: state.counter
|
value: state.counter
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const ConnectedCounter = owl.connect(Counter, mapStoreToProps);
|
increment() {
|
||||||
|
this.env.store.dispatch("increment");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const counter = new ConnectedCounter({ store, qweb });
|
const counter = new Counter({ store, qweb });
|
||||||
```
|
```
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
@@ -214,26 +212,21 @@ const counter = new ConnectedCounter({ store, qweb });
|
|||||||
</button>
|
</button>
|
||||||
```
|
```
|
||||||
|
|
||||||
The arguments of `connect` are:
|
The `ConnectedComponent` class can be configured with the following fields:
|
||||||
|
|
||||||
- `Counter`: an owl `Component` to connect
|
|
||||||
- `mapStoreToProps`: a function that extracts the `props` of the Component
|
- `mapStoreToProps`: a function that extracts the `props` of the Component
|
||||||
from the `state` of the `Store` and returns them as a dict
|
from the `state` of the `Store` and returns them as a dict.
|
||||||
- `options`: dictionary of optional parameters that may contain
|
|
||||||
- `getStore`: a function that takes the `env` in arguments and returns an
|
- `getStore`: a function that takes the `env` in arguments and returns an
|
||||||
instance of `Store` to connect to (if not given, connects to `env.store`)
|
instance of `Store` to connect to (if not given, connects to `env.store`)
|
||||||
- `hashFunction`: the function to use to detect changes in the state (if not
|
- `hashFunction`: the function to use to detect changes in the state (if not
|
||||||
given, generates a function that uses revision numbers, incremented at
|
given, generates a function that uses revision numbers, incremented at
|
||||||
each state change)
|
each state change)
|
||||||
- `deep`: [only useful if no hashFunction is given] if false, only watch
|
- `deep` (boolean): [only useful if no hashFunction is given] if `false`, only watch
|
||||||
for top level state changes (true by default)
|
for top level state changes (`true` by default)
|
||||||
|
|
||||||
The `connect` function returns a sub class of the given `Component` which is
|
|
||||||
connected to the `store`.
|
|
||||||
|
|
||||||
### Semantics
|
### Semantics
|
||||||
|
|
||||||
The `Store` and the `connect` function try to be smart and to optimize as much
|
The `Store` and the `ConnectedComponent` try to be smart and to optimize as much
|
||||||
as possible the rendering and update process. What is important to know is:
|
as possible the rendering and update process. What is important to know is:
|
||||||
|
|
||||||
- components are always updated in the order of their creation (so, parent
|
- components are always updated in the order of their creation (so, parent
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ owl.__info__.mode = "dev";
|
|||||||
Note that templates compiled with the `prod` settings will not be recompiled.
|
Note that templates compiled with the `prod` settings will not be recompiled.
|
||||||
So, changing this setting is best done at startup.
|
So, changing this setting is best done at startup.
|
||||||
|
|
||||||
|
An important job done by the `dev` mode is to validate props for each component
|
||||||
|
creation and update. Also, extra props will cause an error.
|
||||||
|
|
||||||
## Playground
|
## Playground
|
||||||
|
|
||||||
The playground is an important application designed to help learning and
|
The playground is an important application designed to help learning and
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "owl",
|
"name": "owl",
|
||||||
"version": "0.16.0",
|
"version": "0.17.0",
|
||||||
"description": "Odoo Web Library (OWL)",
|
"description": "Odoo Web Library (OWL)",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+75
-92
@@ -1,5 +1,5 @@
|
|||||||
import { Observer } from "./observer";
|
import { Observer } from "./observer";
|
||||||
import { QWeb, CompiledTemplate } from "./qweb_core";
|
import { QWeb, CompiledTemplate, UTILS } from "./qweb_core";
|
||||||
import { h, patch, VNode } from "./vdom";
|
import { h, patch, VNode } from "./vdom";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,9 +119,6 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
if (defaultProps) {
|
if (defaultProps) {
|
||||||
props = this.__applyDefaultProps(props, defaultProps);
|
props = this.__applyDefaultProps(props, defaultProps);
|
||||||
}
|
}
|
||||||
if (QWeb.dev) {
|
|
||||||
this.__validateProps(props || {});
|
|
||||||
}
|
|
||||||
// is this a good idea?
|
// is this a good idea?
|
||||||
// Pro: if props is empty, we can create easily a component
|
// Pro: if props is empty, we can create easily a component
|
||||||
// Con: this is not really safe
|
// Con: this is not really safe
|
||||||
@@ -135,6 +132,11 @@ 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;
|
||||||
|
if (QWeb.dev) {
|
||||||
|
// we only validate props for root widgets here. "Regular" widget
|
||||||
|
// props are validated by the t-component directive
|
||||||
|
UTILS.validateProps(this.constructor, this.props);
|
||||||
|
}
|
||||||
this.env.qweb.on("update", this, () => {
|
this.env.qweb.on("update", this, () => {
|
||||||
if (this.__owl__.isMounted) {
|
if (this.__owl__.isMounted) {
|
||||||
this.render(true);
|
this.render(true);
|
||||||
@@ -241,6 +243,12 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
*/
|
*/
|
||||||
willUnmount() {}
|
willUnmount() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* catchError is a method called whenever some error happens in the rendering or
|
||||||
|
* lifecycle hooks of a child.
|
||||||
|
*/
|
||||||
|
catchError(error: Error): void {}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Public
|
// Public
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -272,7 +280,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async render(force: boolean = false, patchQueue?: any[]): Promise<void> {
|
async render(force: boolean = false, patchQueue?: any[], scope?: any, vars?: any): Promise<void> {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
if (!__owl__.isMounted) {
|
if (!__owl__.isMounted) {
|
||||||
return;
|
return;
|
||||||
@@ -281,7 +289,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
if (shouldPatch) {
|
if (shouldPatch) {
|
||||||
patchQueue = [];
|
patchQueue = [];
|
||||||
}
|
}
|
||||||
const renderVDom = this.__render(force, patchQueue);
|
const renderVDom = this.__render(force, patchQueue, scope, vars);
|
||||||
const renderId = __owl__.renderId;
|
const renderId = __owl__.renderId;
|
||||||
await renderVDom;
|
await renderVDom;
|
||||||
|
|
||||||
@@ -396,7 +404,11 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
for (let key in handlers) {
|
for (let key in handlers) {
|
||||||
handlers[key]();
|
handlers[key]();
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
this.mounted();
|
this.mounted();
|
||||||
|
} catch (e) {
|
||||||
|
errorHandler(e, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__callWillUnmount() {
|
__callWillUnmount() {
|
||||||
@@ -415,7 +427,9 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
async __updateProps(
|
async __updateProps(
|
||||||
nextProps: Props,
|
nextProps: Props,
|
||||||
forceUpdate: boolean = false,
|
forceUpdate: boolean = false,
|
||||||
patchQueue?: any[]
|
patchQueue?: any[],
|
||||||
|
scope?: any,
|
||||||
|
vars?: any
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
@@ -423,12 +437,9 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
if (defaultProps) {
|
if (defaultProps) {
|
||||||
nextProps = this.__applyDefaultProps(nextProps, defaultProps);
|
nextProps = this.__applyDefaultProps(nextProps, defaultProps);
|
||||||
}
|
}
|
||||||
if (QWeb.dev) {
|
|
||||||
this.__validateProps(nextProps);
|
|
||||||
}
|
|
||||||
await this.willUpdateProps(nextProps);
|
await this.willUpdateProps(nextProps);
|
||||||
this.props = nextProps;
|
this.props = nextProps;
|
||||||
await this.render(forceUpdate, patchQueue);
|
await this.render(forceUpdate, patchQueue, scope, vars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,15 +453,20 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
__owl__.vnode = patch(target, vnode);
|
__owl__.vnode = patch(target, vnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
__prepare(): Promise<VNode> {
|
__prepare(scope?: Object, vars?: any): Promise<VNode> {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
__owl__.renderProps = this.props;
|
__owl__.renderProps = this.props;
|
||||||
__owl__.renderPromise = this.__prepareAndRender();
|
__owl__.renderPromise = this.__prepareAndRender(scope, vars);
|
||||||
return __owl__.renderPromise;
|
return __owl__.renderPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async __prepareAndRender(): Promise<VNode> {
|
async __prepareAndRender(scope?: Object, vars?: any): Promise<VNode> {
|
||||||
|
try {
|
||||||
await this.willStart();
|
await this.willStart();
|
||||||
|
} catch (e) {
|
||||||
|
errorHandler(e, this);
|
||||||
|
return Promise.resolve(h("div"));
|
||||||
|
}
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
if (__owl__.isDestroyed) {
|
if (__owl__.isDestroyed) {
|
||||||
return Promise.resolve(h("div"));
|
return Promise.resolve(h("div"));
|
||||||
@@ -481,10 +497,15 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
}
|
}
|
||||||
__owl__.render = qweb.render.bind(qweb, this.template);
|
__owl__.render = qweb.render.bind(qweb, this.template);
|
||||||
this.__observeState();
|
this.__observeState();
|
||||||
return this.__render();
|
return this.__render(false, [], scope, vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
async __render(force: boolean = false, patchQueue: any[] = []): Promise<VNode> {
|
async __render(
|
||||||
|
force: boolean = false,
|
||||||
|
patchQueue: any[] = [],
|
||||||
|
scope?: Object,
|
||||||
|
vars?: any
|
||||||
|
): Promise<VNode> {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
__owl__.renderId++;
|
__owl__.renderId++;
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
@@ -495,13 +516,21 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
if (__owl__.observer) {
|
if (__owl__.observer) {
|
||||||
__owl__.observer.allowMutations = false;
|
__owl__.observer.allowMutations = false;
|
||||||
}
|
}
|
||||||
let vnode = __owl__.render!(this, {
|
let vnode;
|
||||||
|
try {
|
||||||
|
vnode = __owl__.render!(this, {
|
||||||
promises,
|
promises,
|
||||||
handlers: __owl__.boundHandlers,
|
handlers: __owl__.boundHandlers,
|
||||||
mountedHandlers: __owl__.mountedHandlers,
|
mountedHandlers: __owl__.mountedHandlers,
|
||||||
forceUpdate: force,
|
forceUpdate: force,
|
||||||
patchQueue
|
patchQueue,
|
||||||
|
scope,
|
||||||
|
vars
|
||||||
});
|
});
|
||||||
|
} catch (e) {
|
||||||
|
vnode = __owl__.vnode || h("div");
|
||||||
|
errorHandler(e, this);
|
||||||
|
}
|
||||||
patch.push(vnode);
|
patch.push(vnode);
|
||||||
if (__owl__.observer) {
|
if (__owl__.observer) {
|
||||||
__owl__.observer.allowMutations = true;
|
__owl__.observer.allowMutations = true;
|
||||||
@@ -577,9 +606,12 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
* 3) Call 'patched' on the component of each patch, in inverse order
|
* 3) Call 'patched' on the component of each patch, in inverse order
|
||||||
*/
|
*/
|
||||||
__applyPatchQueue(patchQueue: any[]) {
|
__applyPatchQueue(patchQueue: any[]) {
|
||||||
|
let component = this;
|
||||||
|
try {
|
||||||
const patchLen = patchQueue.length;
|
const patchLen = patchQueue.length;
|
||||||
for (let i = 0; i < patchLen; i++) {
|
for (let i = 0; i < patchLen; i++) {
|
||||||
const patch = patchQueue[i];
|
const patch = patchQueue[i];
|
||||||
|
component = patch[0];
|
||||||
patch.push(patch[0].willPatch());
|
patch.push(patch[0].willPatch());
|
||||||
}
|
}
|
||||||
for (let i = 0; i < patchLen; i++) {
|
for (let i = 0; i < patchLen; i++) {
|
||||||
@@ -588,85 +620,36 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
}
|
}
|
||||||
for (let i = patchLen - 1; i >= 0; i--) {
|
for (let i = patchLen - 1; i >= 0; i--) {
|
||||||
const patch = patchQueue[i];
|
const patch = patchQueue[i];
|
||||||
|
component = patch[0];
|
||||||
patch[0].patched(patch[2]);
|
patch[0].patched(patch[2]);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
errorHandler(e, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
//------------------------------------------------------------------------------
|
||||||
* Validate the component props (or next props) against the (static) props
|
// Error handling
|
||||||
* description. This is potentially an expensive operation: it may needs to
|
//------------------------------------------------------------------------------
|
||||||
* visit recursively the props and all the children to check if they are valid.
|
|
||||||
* This is why it is only done in 'dev' mode.
|
function errorHandler(error, component) {
|
||||||
*/
|
let canCatch = false;
|
||||||
__validateProps(props: Object) {
|
let qweb = component.env.qweb;
|
||||||
const propsDef = (<any>this.constructor).props;
|
let root = component;
|
||||||
if (propsDef instanceof Array) {
|
while (component && !(canCatch = component.catchError !== Component.prototype.catchError)) {
|
||||||
// list of strings (prop names)
|
root = component;
|
||||||
for (let i = 0, l = propsDef.length; i < l; i++) {
|
component = component.__owl__.parent;
|
||||||
if (!(propsDef[i] in props)) {
|
|
||||||
throw new Error(`Missing props '${propsDef[i]}' (component '${this.constructor.name}')`);
|
|
||||||
}
|
}
|
||||||
}
|
console.error(error);
|
||||||
} else if (propsDef) {
|
// we trigger error on QWeb so it can be logged/handled
|
||||||
// propsDef is an object now
|
qweb.trigger("error", error);
|
||||||
for (let propName in propsDef) {
|
|
||||||
if (!(propName in props)) {
|
if (canCatch) {
|
||||||
if (propsDef[propName] && !propsDef[propName].optional) {
|
setTimeout(() => {
|
||||||
throw new Error(`Missing props '${propName}' (component '${this.constructor.name}')`);
|
component.catchError(error);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
break;
|
root.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let isValid = isValidProp(props[propName], propsDef[propName]);
|
|
||||||
if (!isValid) {
|
|
||||||
throw new Error(
|
|
||||||
`Props '${propName}' of invalid type in component '${this.constructor.name}'`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Prop validation helper
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if an invidual prop value matches its (static) prop definition
|
|
||||||
*/
|
|
||||||
function isValidProp(prop, propDef): boolean {
|
|
||||||
if (typeof propDef === "function") {
|
|
||||||
// Check if a value is constructed by some Constructor. Note that there is a
|
|
||||||
// slight abuse of language: we want to consider primitive values as well.
|
|
||||||
//
|
|
||||||
// So, even though 1 is not an instance of Number, we want to consider that
|
|
||||||
// it is valid.
|
|
||||||
if (typeof prop === "object") {
|
|
||||||
return prop instanceof propDef;
|
|
||||||
}
|
|
||||||
return typeof prop === propDef.name.toLowerCase();
|
|
||||||
} else if (propDef instanceof Array) {
|
|
||||||
// If this code is executed, this means that we want to check if a prop
|
|
||||||
// matches at least one of its descriptor.
|
|
||||||
let result = false;
|
|
||||||
for (let i = 0, iLen = propDef.length; i < iLen; i++) {
|
|
||||||
result = result || isValidProp(prop, propDef[i]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// propsDef is an object
|
|
||||||
let result = isValidProp(prop, propDef.type);
|
|
||||||
if (propDef.type === Array) {
|
|
||||||
for (let i = 0, iLen = prop.length; i < iLen; i++) {
|
|
||||||
result = result && isValidProp(prop[i], propDef.element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (propDef.type === Object) {
|
|
||||||
const shape = propDef.shape;
|
|
||||||
for (let key in shape) {
|
|
||||||
result = result && isValidProp(prop[key], shape[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ import "./qweb_extensions";
|
|||||||
import { QWeb } from "./qweb_core";
|
import { QWeb } from "./qweb_core";
|
||||||
export { QWeb };
|
export { QWeb };
|
||||||
|
|
||||||
export { connect, Store } from "./store";
|
export { Store, ConnectedComponent } from "./store";
|
||||||
import * as _utils from "./utils";
|
import * as _utils from "./utils";
|
||||||
|
|
||||||
export const __info__ = {};
|
export const __info__ = {};
|
||||||
|
|||||||
+117
-27
@@ -1,4 +1,4 @@
|
|||||||
import { VNode, h } from "./vdom";
|
import { VNode, h, patch } from "./vdom";
|
||||||
import { QWebVar, compileExpr } from "./qweb_expressions";
|
import { QWebVar, compileExpr } from "./qweb_expressions";
|
||||||
import { EventBus } from "./event_bus";
|
import { EventBus } from "./event_bus";
|
||||||
|
|
||||||
@@ -117,7 +117,27 @@ function parseXML(xml: string): Document {
|
|||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(xml, "text/xml");
|
const doc = parser.parseFromString(xml, "text/xml");
|
||||||
if (doc.getElementsByTagName("parsererror").length) {
|
if (doc.getElementsByTagName("parsererror").length) {
|
||||||
throw new Error("Invalid XML in template");
|
let msg = "Invalid XML in template.";
|
||||||
|
const parsererrorText = doc.getElementsByTagName("parsererror")[0].textContent;
|
||||||
|
if (parsererrorText) {
|
||||||
|
msg += "\nThe parser has produced the following error message:\n" + parsererrorText;
|
||||||
|
const re = /\d+/g;
|
||||||
|
const firstMatch = re.exec(parsererrorText);
|
||||||
|
if (firstMatch) {
|
||||||
|
const lineNumber = Number(firstMatch[0]);
|
||||||
|
const line = xml.split("\n")[lineNumber - 1];
|
||||||
|
const secondMatch = re.exec(parsererrorText);
|
||||||
|
if (line && secondMatch) {
|
||||||
|
const columnIndex = Number(secondMatch[0]) - 1;
|
||||||
|
if (line[columnIndex]) {
|
||||||
|
msg +=
|
||||||
|
`\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\n` +
|
||||||
|
`${line}\n${"-".repeat(columnIndex - 1)}^`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
@@ -268,45 +288,66 @@ export class QWeb extends EventBus {
|
|||||||
return template.fn.call(this, context, extra);
|
return template.fn.call(this, context, extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
_compile(name: string, elem: Element, parentNode?: number): CompiledTemplate {
|
/**
|
||||||
|
* Render a template to a html string.
|
||||||
|
*
|
||||||
|
* Note that this is more limited than the `render` method: it is not suitable
|
||||||
|
* to render a full component tree, since this is an asynchronous operation.
|
||||||
|
* This method can only render templates without components.
|
||||||
|
*/
|
||||||
|
renderToString(name: string, context: EvalContext = {}): string {
|
||||||
|
const vnode = this.render(name, context);
|
||||||
|
if (vnode.sel === undefined) {
|
||||||
|
return vnode.text!;
|
||||||
|
}
|
||||||
|
const node = document.createElement(vnode.sel);
|
||||||
|
const result = patch(node, vnode);
|
||||||
|
return (<HTMLElement>result.elm).outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
_compile(name: string, elem: Element, parentContext?: Context): CompiledTemplate {
|
||||||
const isDebug = elem.attributes.hasOwnProperty("t-debug");
|
const isDebug = elem.attributes.hasOwnProperty("t-debug");
|
||||||
const ctx = new Context(name);
|
const ctx = new Context(name);
|
||||||
if (parentNode) {
|
if (parentContext) {
|
||||||
ctx.nextID = parentNode + 1;
|
ctx.variables = Object.create(parentContext.variables);
|
||||||
ctx.parentNode = parentNode;
|
ctx.nextID = parentContext.parentNode! + 1;
|
||||||
|
ctx.parentNode = parentContext.parentNode!;
|
||||||
ctx.allowMultipleRoots = true;
|
ctx.allowMultipleRoots = true;
|
||||||
ctx.addLine(`let c${parentNode} = extra.parentNode;`);
|
ctx.hasParentWidget = true;
|
||||||
|
ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`);
|
||||||
|
|
||||||
|
for (let v in parentContext.variables) {
|
||||||
|
let variable = <any>parentContext.variables[v];
|
||||||
|
if (variable.id) {
|
||||||
|
ctx.addLine(`let ${variable.id} = extra.vars.${variable.id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parentContext) {
|
||||||
|
ctx.addLine(" Object.assign(context, extra.scope);");
|
||||||
}
|
}
|
||||||
this._compileNode(elem, ctx);
|
this._compileNode(elem, ctx);
|
||||||
|
|
||||||
if (ctx.shouldProtectContext) {
|
if (!parentContext) {
|
||||||
ctx.code.unshift(" context = Object.create(context);");
|
if (ctx.shouldDefineResult) {
|
||||||
}
|
ctx.addLine(`return result;`);
|
||||||
if (ctx.shouldDefineOwner) {
|
} else {
|
||||||
// this is necessary to prevent some directives (t-forach for ex) to
|
|
||||||
// pollute the rendering context by adding some keys in it.
|
|
||||||
ctx.code.unshift(" let owner = context;");
|
|
||||||
}
|
|
||||||
if (ctx.shouldDefineQWeb) {
|
|
||||||
ctx.code.unshift(" let QWeb = this.constructor;");
|
|
||||||
}
|
|
||||||
if (ctx.shouldDefineUtils) {
|
|
||||||
ctx.code.unshift(" let utils = this.utils;");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parentNode) {
|
|
||||||
if (!ctx.rootNode) {
|
if (!ctx.rootNode) {
|
||||||
throw new Error("A template should have one root node");
|
throw new Error(`A template should have one root node (${ctx.templateName})`);
|
||||||
}
|
}
|
||||||
ctx.addLine(`return vn${ctx.rootNode};`);
|
ctx.addLine(`return vn${ctx.rootNode};`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let code = ctx.generateCode();
|
||||||
|
|
||||||
let template;
|
let template;
|
||||||
try {
|
try {
|
||||||
template = new Function("context", "extra", ctx.code.join("\n")) as CompiledTemplate;
|
template = new Function("context", "extra", code.join("\n")) as CompiledTemplate;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const templateName = ctx.templateName.replace(/`/g, "'");
|
const templateName = ctx.templateName.replace(/`/g, "'");
|
||||||
console.groupCollapsed(`Invalid Code generated by ${templateName}`);
|
console.groupCollapsed(`Invalid Code generated by ${templateName}`);
|
||||||
console.warn(ctx.code.join("\n"));
|
console.warn(code.join("\n"));
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid generated code while compiling template '${templateName}': ${e.message}`
|
`Invalid generated code while compiling template '${templateName}': ${e.message}`
|
||||||
@@ -649,13 +690,18 @@ export class Context {
|
|||||||
rootContext: Context;
|
rootContext: Context;
|
||||||
caller: Element | undefined;
|
caller: Element | undefined;
|
||||||
shouldDefineOwner: boolean = false;
|
shouldDefineOwner: boolean = false;
|
||||||
|
shouldDefineParent: boolean = false;
|
||||||
shouldDefineQWeb: boolean = false;
|
shouldDefineQWeb: boolean = false;
|
||||||
shouldDefineUtils: boolean = false;
|
shouldDefineUtils: boolean = false;
|
||||||
|
shouldDefineResult: boolean = false;
|
||||||
shouldProtectContext: boolean = false;
|
shouldProtectContext: boolean = false;
|
||||||
|
shouldTrackScope: boolean = false;
|
||||||
inLoop: boolean = false;
|
inLoop: boolean = false;
|
||||||
inPreTag: boolean = false;
|
inPreTag: boolean = false;
|
||||||
templateName: string;
|
templateName: string;
|
||||||
allowMultipleRoots: boolean = false;
|
allowMultipleRoots: boolean = false;
|
||||||
|
hasParentWidget: boolean = false;
|
||||||
|
scopeVars: any[] = [];
|
||||||
|
|
||||||
constructor(name?: string) {
|
constructor(name?: string) {
|
||||||
this.rootContext = this;
|
this.rootContext = this;
|
||||||
@@ -668,6 +714,44 @@ export class Context {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateCode(): string[] {
|
||||||
|
const shouldTrackScope = this.shouldTrackScope && this.scopeVars.length;
|
||||||
|
if (shouldTrackScope) {
|
||||||
|
// add some vars to scope if needed
|
||||||
|
for (let scopeVar of this.scopeVars.reverse()) {
|
||||||
|
let { index, key, indent } = scopeVar;
|
||||||
|
const prefix = new Array(indent + 2).join(" ");
|
||||||
|
this.code.splice(index + 1, 0, prefix + `scope.${key} = context.${key};`);
|
||||||
|
}
|
||||||
|
this.code.unshift(" const scope = Object.create(null);");
|
||||||
|
}
|
||||||
|
if (this.shouldProtectContext) {
|
||||||
|
this.code.unshift(" context = Object.create(context);");
|
||||||
|
}
|
||||||
|
if (this.shouldDefineResult) {
|
||||||
|
this.code.unshift(" let result;");
|
||||||
|
}
|
||||||
|
if (this.shouldDefineOwner) {
|
||||||
|
// this is necessary to prevent some directives (t-forach for ex) to
|
||||||
|
// pollute the rendering context by adding some keys in it.
|
||||||
|
this.code.unshift(" let owner = context;");
|
||||||
|
}
|
||||||
|
if (this.shouldDefineParent) {
|
||||||
|
if (this.hasParentWidget) {
|
||||||
|
this.code.unshift(" let parent = extra.parent;");
|
||||||
|
} else {
|
||||||
|
this.code.unshift(" let parent = context;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.shouldDefineQWeb) {
|
||||||
|
this.code.unshift(" let QWeb = this.constructor;");
|
||||||
|
}
|
||||||
|
if (this.shouldDefineUtils) {
|
||||||
|
this.code.unshift(" let utils = this.utils;");
|
||||||
|
}
|
||||||
|
return this.code;
|
||||||
|
}
|
||||||
|
|
||||||
withParent(node: number): Context {
|
withParent(node: number): Context {
|
||||||
if (
|
if (
|
||||||
!this.allowMultipleRoots &&
|
!this.allowMultipleRoots &&
|
||||||
@@ -696,9 +780,15 @@ export class Context {
|
|||||||
this.indentLevel--;
|
this.indentLevel--;
|
||||||
}
|
}
|
||||||
|
|
||||||
addLine(line: string) {
|
addLine(line: string): number {
|
||||||
const prefix = new Array(this.indentLevel + 2).join(" ");
|
const prefix = new Array(this.indentLevel + 2).join(" ");
|
||||||
this.code.push(prefix + line);
|
this.code.push(prefix + line);
|
||||||
|
return this.code.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
addToScope(key: string, expr: string) {
|
||||||
|
const index = this.addLine(`context.${key} = ${expr};`);
|
||||||
|
this.rootContext.scopeVars.push({ index, key, indent: this.indentLevel });
|
||||||
}
|
}
|
||||||
|
|
||||||
addIf(condition: string) {
|
addIf(condition: string) {
|
||||||
|
|||||||
@@ -255,11 +255,11 @@ QWeb.addDirective({
|
|||||||
ctx.addLine(`var _length${keysID} = _${keysID}.length;`);
|
ctx.addLine(`var _length${keysID} = _${keysID}.length;`);
|
||||||
ctx.addLine(`for (let i = 0; i < _length${keysID}; i++) {`);
|
ctx.addLine(`for (let i = 0; i < _length${keysID}; i++) {`);
|
||||||
ctx.indent();
|
ctx.indent();
|
||||||
ctx.addLine(`context.${name}_first = i === 0;`);
|
ctx.addToScope(name + '_first', 'i === 0');
|
||||||
ctx.addLine(`context.${name}_last = i === _length${keysID} - 1;`);
|
ctx.addToScope(name + '_last', `i === _length${keysID} - 1`);
|
||||||
ctx.addLine(`context.${name}_index = i;`);
|
ctx.addToScope(name + '_index', 'i');
|
||||||
ctx.addLine(`context.${name} = _${keysID}[i];`);
|
ctx.addToScope(name, `_${keysID}[i]`);
|
||||||
ctx.addLine(`context.${name}_value = _${valuesID}[i];`);
|
ctx.addToScope(name + '_value', `_${valuesID}[i]`);
|
||||||
const nodeCopy = <Element>node.cloneNode(true);
|
const nodeCopy = <Element>node.cloneNode(true);
|
||||||
let shouldWarn = nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key");
|
let shouldWarn = nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key");
|
||||||
if (!shouldWarn && node.tagName === "t") {
|
if (!shouldWarn && node.tagName === "t") {
|
||||||
|
|||||||
+160
-17
@@ -190,6 +190,19 @@ const T_COMPONENT_MODS_CODE = Object.assign({}, MODS_CODE, {
|
|||||||
self: "if (e.target !== vn.elm) {return}"
|
self: "if (e.target !== vn.elm) {return}"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
UTILS.defineProxy = function defineProxy(target, source) {
|
||||||
|
for (let k in source) {
|
||||||
|
Object.defineProperty(target, k, {
|
||||||
|
get() {
|
||||||
|
return source[k];
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
source[k] = val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The t-component directive is certainly a complicated and hard to maintain piece
|
* The t-component directive is certainly a complicated and hard to maintain piece
|
||||||
* of code. To help you, fellow developer, if you have to maintain it, I offer
|
* of code. To help you, fellow developer, if you have to maintain it, I offer
|
||||||
@@ -359,6 +372,7 @@ QWeb.addDirective({
|
|||||||
ctx.addLine("//COMPONENT");
|
ctx.addLine("//COMPONENT");
|
||||||
ctx.rootContext.shouldDefineOwner = true;
|
ctx.rootContext.shouldDefineOwner = true;
|
||||||
ctx.rootContext.shouldDefineQWeb = true;
|
ctx.rootContext.shouldDefineQWeb = true;
|
||||||
|
ctx.rootContext.shouldDefineParent = true;
|
||||||
ctx.rootContext.shouldDefineUtils = true;
|
ctx.rootContext.shouldDefineUtils = true;
|
||||||
let keepAlive = node.getAttribute("t-keepalive") ? true : false;
|
let keepAlive = node.getAttribute("t-keepalive") ? true : false;
|
||||||
let async = node.getAttribute("t-asyncroot") ? true : false;
|
let async = node.getAttribute("t-asyncroot") ? true : false;
|
||||||
@@ -471,7 +485,7 @@ QWeb.addDirective({
|
|||||||
if (tattClass) {
|
if (tattClass) {
|
||||||
let tattExpr = ctx.formatExpression(tattClass);
|
let tattExpr = ctx.formatExpression(tattClass);
|
||||||
if (tattExpr[0] !== "{" || tattExpr[tattExpr.length - 1] !== "}") {
|
if (tattExpr[0] !== "{" || tattExpr[tattExpr.length - 1] !== "}") {
|
||||||
tattExpr = `this.utils.toObj(${tattExpr})`;
|
tattExpr = `utils.toObj(${tattExpr})`;
|
||||||
}
|
}
|
||||||
if (classAttr) {
|
if (classAttr) {
|
||||||
ctx.addLine(`Object.assign(${classObj}, ${tattExpr})`);
|
ctx.addLine(`Object.assign(${classObj}, ${tattExpr})`);
|
||||||
@@ -516,16 +530,28 @@ QWeb.addDirective({
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`let w${componentID} = ${templateID} in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[${templateID}]] : false;`
|
`let w${componentID} = ${templateID} in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[${templateID}]] : false;`
|
||||||
);
|
);
|
||||||
|
if (ctx.parentNode) {
|
||||||
ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`);
|
ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`);
|
||||||
|
}
|
||||||
|
let shouldProxy = false;
|
||||||
if (async) {
|
if (async) {
|
||||||
ctx.addLine(`const patchQueue${componentID} = [];`);
|
ctx.addLine(`const patchQueue${componentID} = [];`);
|
||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`c${ctx.parentNode}.push(w${componentID} && w${componentID}.__owl__.pvnode || null);`
|
`c${ctx.parentNode}.push(w${componentID} && w${componentID}.__owl__.pvnode || null);`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
if (ctx.parentNode) {
|
||||||
ctx.addLine(`c${ctx.parentNode}.push(null);`);
|
ctx.addLine(`c${ctx.parentNode}.push(null);`);
|
||||||
|
} else {
|
||||||
|
let id = ctx.generateID();
|
||||||
|
ctx.rootContext.rootNode = id;
|
||||||
|
shouldProxy = true;
|
||||||
|
ctx.rootContext.shouldDefineResult = true;
|
||||||
|
ctx.addLine(`let vn${id} = {};`);
|
||||||
|
ctx.addLine(`result = vn${id};`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.addLine(`let props${componentID} = {${propStr}};`);
|
ctx.addLine(`let props${componentID} = {${propStr}};`);
|
||||||
ctx.addIf(
|
ctx.addIf(
|
||||||
@@ -545,16 +571,27 @@ QWeb.addDirective({
|
|||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`let W${componentID} = context.components && context.components[componentKey${componentID}] || QWeb.components[componentKey${componentID}];`
|
`let W${componentID} = context.components && context.components[componentKey${componentID}] || QWeb.components[componentKey${componentID}];`
|
||||||
);
|
);
|
||||||
|
|
||||||
// maybe only do this in dev mode...
|
// maybe only do this in dev mode...
|
||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}`
|
`if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}`
|
||||||
);
|
);
|
||||||
ctx.addLine(`w${componentID} = new W${componentID}(owner, props${componentID});`);
|
if (QWeb.dev) {
|
||||||
ctx.addLine(`context.__owl__.cmap[${templateID}] = w${componentID}.__owl__.id;`);
|
ctx.addLine(`utils.validateProps(W${componentID}, props${componentID})`);
|
||||||
|
}
|
||||||
|
ctx.addLine(`w${componentID} = new W${componentID}(parent, props${componentID});`);
|
||||||
|
ctx.addLine(`parent.__owl__.cmap[${templateID}] = w${componentID}.__owl__.id;`);
|
||||||
|
|
||||||
// SLOTS
|
// SLOTS
|
||||||
if (node.childNodes.length) {
|
const varDefs: string[] = [];
|
||||||
|
const hasSlots = node.childNodes.length;
|
||||||
|
if (hasSlots) {
|
||||||
|
ctx.rootContext.shouldTrackScope = true;
|
||||||
|
for (let v of Object.values(ctx.variables)) {
|
||||||
|
if (v["id"]) {
|
||||||
|
varDefs.push(v["id"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const clone = <Element>node.cloneNode(true);
|
const clone = <Element>node.cloneNode(true);
|
||||||
const slotNodes = clone.querySelectorAll("[t-set]");
|
const slotNodes = clone.querySelectorAll("[t-set]");
|
||||||
const slotId = qweb.nextSlotId++;
|
const slotId = qweb.nextSlotId++;
|
||||||
@@ -565,7 +602,7 @@ QWeb.addDirective({
|
|||||||
slotNode.parentElement!.removeChild(slotNode);
|
slotNode.parentElement!.removeChild(slotNode);
|
||||||
const key = slotNode.getAttribute("t-set")!;
|
const key = slotNode.getAttribute("t-set")!;
|
||||||
slotNode.removeAttribute("t-set");
|
slotNode.removeAttribute("t-set");
|
||||||
const slotFn = qweb._compile(`slot_${key}_template`, slotNode, ctx.parentNode!);
|
const slotFn = qweb._compile(`slot_${key}_template`, slotNode, ctx);
|
||||||
qweb.slots[`${slotId}_${key}`] = slotFn.bind(qweb);
|
qweb.slots[`${slotId}_${key}`] = slotFn.bind(qweb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -574,24 +611,37 @@ QWeb.addDirective({
|
|||||||
for (let child of Object.values(clone.childNodes)) {
|
for (let child of Object.values(clone.childNodes)) {
|
||||||
t.appendChild(child);
|
t.appendChild(child);
|
||||||
}
|
}
|
||||||
const slotFn = qweb._compile(`slot_default_template`, t, ctx.parentNode!);
|
const slotFn = qweb._compile(`slot_default_template`, t, ctx);
|
||||||
qweb.slots[`${slotId}_default`] = slotFn.bind(qweb);
|
qweb.slots[`${slotId}_default`] = slotFn.bind(qweb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.addLine(`def${defID} = w${componentID}.__prepare();`);
|
let scopeVars = "";
|
||||||
|
if (hasSlots) {
|
||||||
|
scopeVars += ctx.scopeVars.length ? `Object.assign({}, scope)` : varDefs.length ? `{}` : "";
|
||||||
|
if (varDefs.length) {
|
||||||
|
scopeVars += `, {${varDefs.join(",")}}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.addLine(`def${defID} = w${componentID}.__prepare(${scopeVars});`);
|
||||||
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
||||||
|
let registerCode = `c${ctx.parentNode}[_${dummyID}_index]=pvnode;`;
|
||||||
|
if (shouldProxy) {
|
||||||
|
registerCode = `utils.defineProxy(vn${ctx.rootNode}, pvnode);`;
|
||||||
|
}
|
||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`def${defID} = def${defID}.then(vnode=>{${createHook}let pvnode=h(vnode.sel, {key: ${templateID}, hook: {insert(vn) {let nvn=w${componentID}.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});c${
|
`def${defID} = def${defID}.then(vnode=>{${createHook}let pvnode=h(vnode.sel, {key: ${templateID}, hook: {insert(vn) {let nvn=w${componentID}.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});${registerCode}w${componentID}.__owl__.pvnode = pvnode;});`
|
||||||
ctx.parentNode
|
|
||||||
}[_${dummyID}_index]=pvnode;w${componentID}.__owl__.pvnode = pvnode;});`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.addElse();
|
ctx.addElse();
|
||||||
// need to update component
|
// need to update component
|
||||||
const patchQueueCode = async ? `patchQueue${componentID}` : "extra.patchQueue";
|
const patchQueueCode = async ? `patchQueue${componentID}` : "extra.patchQueue";
|
||||||
|
if (QWeb.dev) {
|
||||||
|
ctx.addLine(`utils.validateProps(w${componentID}.constructor, props${componentID})`);
|
||||||
|
}
|
||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, extra.forceUpdate, ${patchQueueCode});`
|
`def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, extra.forceUpdate, ${patchQueueCode}${scopeVars &&
|
||||||
|
", " + scopeVars});`
|
||||||
);
|
);
|
||||||
let keepAliveCode = "";
|
let keepAliveCode = "";
|
||||||
if (keepAlive) {
|
if (keepAlive) {
|
||||||
@@ -600,9 +650,7 @@ QWeb.addDirective({
|
|||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`def${defID} = def${defID}.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};${
|
`def${defID} = def${defID}.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};${
|
||||||
tattStyle ? `w${componentID}.el.style=${tattStyle};` : ""
|
tattStyle ? `w${componentID}.el.style=${tattStyle};` : ""
|
||||||
}let pvnode=w${componentID}.__owl__.pvnode;${keepAliveCode}c${
|
}let pvnode=w${componentID}.__owl__.pvnode;${keepAliveCode}${registerCode}});`
|
||||||
ctx.parentNode
|
|
||||||
}[_${dummyID}_index]=pvnode;});`
|
|
||||||
);
|
);
|
||||||
ctx.closeIf();
|
ctx.closeIf();
|
||||||
|
|
||||||
@@ -626,6 +674,100 @@ QWeb.addDirective({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Prop validation helper
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the component props (or next props) against the (static) props
|
||||||
|
* description. This is potentially an expensive operation: it may needs to
|
||||||
|
* visit recursively the props and all the children to check if they are valid.
|
||||||
|
* This is why it is only done in 'dev' mode.
|
||||||
|
*/
|
||||||
|
UTILS.validateProps = function(Widget, props: Object) {
|
||||||
|
const propsDef = (<any>Widget).props;
|
||||||
|
if (propsDef instanceof Array) {
|
||||||
|
// list of strings (prop names)
|
||||||
|
for (let i = 0, l = propsDef.length; i < l; i++) {
|
||||||
|
const propName = propsDef[i];
|
||||||
|
if (propName[propName.length - 1] === "?") {
|
||||||
|
// optional prop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!props[propName]) {
|
||||||
|
throw new Error(`Missing props '${propsDef[i]}' (component '${Widget.name}')`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let key in props) {
|
||||||
|
if (!propsDef.includes(key) && !propsDef.includes(key + "?")) {
|
||||||
|
throw new Error(`Unknown prop '${key}' given to component '${Widget.name}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (propsDef) {
|
||||||
|
// propsDef is an object now
|
||||||
|
for (let propName in propsDef) {
|
||||||
|
if (props[propName] === undefined) {
|
||||||
|
if (propsDef[propName] && !propsDef[propName].optional) {
|
||||||
|
throw new Error(`Missing props '${propName}' (component '${Widget.name}')`);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let isValid = isValidProp(props[propName], propsDef[propName]);
|
||||||
|
if (!isValid) {
|
||||||
|
throw new Error(`Props '${propName}' of invalid type in component '${Widget.name}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let propName in props) {
|
||||||
|
if (!(propName in propsDef)) {
|
||||||
|
throw new Error(`Unknown prop '${propName}' given to component '${Widget.name}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an invidual prop value matches its (static) prop definition
|
||||||
|
*/
|
||||||
|
function isValidProp(prop, propDef): boolean {
|
||||||
|
if (propDef === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeof propDef === "function") {
|
||||||
|
// Check if a value is constructed by some Constructor. Note that there is a
|
||||||
|
// slight abuse of language: we want to consider primitive values as well.
|
||||||
|
//
|
||||||
|
// So, even though 1 is not an instance of Number, we want to consider that
|
||||||
|
// it is valid.
|
||||||
|
if (typeof prop === "object") {
|
||||||
|
return prop instanceof propDef;
|
||||||
|
}
|
||||||
|
return typeof prop === propDef.name.toLowerCase();
|
||||||
|
} else if (propDef instanceof Array) {
|
||||||
|
// If this code is executed, this means that we want to check if a prop
|
||||||
|
// matches at least one of its descriptor.
|
||||||
|
let result = false;
|
||||||
|
for (let i = 0, iLen = propDef.length; i < iLen; i++) {
|
||||||
|
result = result || isValidProp(prop, propDef[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// propsDef is an object
|
||||||
|
let result = isValidProp(prop, propDef.type);
|
||||||
|
if (propDef.type === Array) {
|
||||||
|
for (let i = 0, iLen = prop.length; i < iLen; i++) {
|
||||||
|
result = result && isValidProp(prop[i], propDef.element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (propDef.type === Object) {
|
||||||
|
const shape = propDef.shape;
|
||||||
|
for (let key in shape) {
|
||||||
|
result = result && isValidProp(prop[key], shape[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// t-mounted
|
// t-mounted
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -670,12 +812,13 @@ QWeb.addDirective({
|
|||||||
priority: 80,
|
priority: 80,
|
||||||
atNodeEncounter({ ctx, value }): boolean {
|
atNodeEncounter({ ctx, value }): boolean {
|
||||||
const slotKey = ctx.generateID();
|
const slotKey = ctx.generateID();
|
||||||
|
ctx.rootContext.shouldDefineOwner = true;
|
||||||
ctx.addLine(`const slot${slotKey} = this.slots[context.__owl__.slotId + '_' + '${value}'];`);
|
ctx.addLine(`const slot${slotKey} = this.slots[context.__owl__.slotId + '_' + '${value}'];`);
|
||||||
ctx.addIf(`slot${slotKey}`);
|
ctx.addIf(`slot${slotKey}`);
|
||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`slot${slotKey}(context.__owl__.parent, Object.assign({}, extra, {parentNode: c${
|
`slot${slotKey}(context.__owl__.parent, Object.assign({}, extra, {parentNode: c${
|
||||||
ctx.parentNode
|
ctx.parentNode
|
||||||
}}));`
|
}, vars: extra.vars, parent: owner}));`
|
||||||
);
|
);
|
||||||
ctx.closeIf();
|
ctx.closeIf();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+26
-43
@@ -7,7 +7,7 @@ import { Observer } from "./observer";
|
|||||||
*
|
*
|
||||||
* We have here:
|
* We have here:
|
||||||
* - a Store class
|
* - a Store class
|
||||||
* - a connect function
|
* - the ConnectedComponent class
|
||||||
*
|
*
|
||||||
* The Owl store is our answer to the problem of managing complex state across
|
* The Owl store is our answer to the problem of managing complex state across
|
||||||
* components. The main idea is that the store owns some state, allow external
|
* components. The main idea is that the store owns some state, allow external
|
||||||
@@ -185,37 +185,24 @@ function deepRevNumber<T extends Object>(o: T): number {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Constructor<T> = new (...args: any[]) => T;
|
|
||||||
interface EnvWithStore extends Env {
|
|
||||||
store: Store;
|
|
||||||
}
|
|
||||||
type HashFunction = (a: any, b: any) => number;
|
type HashFunction = (a: any, b: any) => number;
|
||||||
interface StoreOptions {
|
|
||||||
getStore?(Env): Store;
|
export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S> {
|
||||||
hashFunction?: HashFunction;
|
deep: boolean = true;
|
||||||
deep?: boolean;
|
getStore(env) {
|
||||||
|
return env.store;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function connect<E extends EnvWithStore, P, S>(
|
hashFunction: HashFunction = ({ storeProps }, options) => {
|
||||||
Comp: Constructor<Component<E, P, S>>,
|
let refFunction = this.deep ? deepRevNumber : revNumber;
|
||||||
mapStoreToProps,
|
|
||||||
options: StoreOptions = <StoreOptions>{}
|
|
||||||
) {
|
|
||||||
let hashFunction = options.hashFunction || null;
|
|
||||||
const getStore = options.getStore || (env => env.store);
|
|
||||||
|
|
||||||
if (!hashFunction) {
|
|
||||||
let deep = "deep" in options ? options.deep : true;
|
|
||||||
let defaultRevFunction = deep ? deepRevNumber : revNumber;
|
|
||||||
hashFunction = function({ storeProps }, options) {
|
|
||||||
const { currentStoreProps } = options;
|
|
||||||
if ("__owl__" in storeProps) {
|
if ("__owl__" in storeProps) {
|
||||||
return defaultRevFunction(storeProps);
|
return refFunction(storeProps);
|
||||||
}
|
}
|
||||||
|
const { currentStoreProps } = options;
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
for (let key in storeProps) {
|
for (let key in storeProps) {
|
||||||
const val = storeProps[key];
|
const val = storeProps[key];
|
||||||
const hashVal = defaultRevFunction(val);
|
const hashVal = refFunction(val);
|
||||||
if (hashVal === 0) {
|
if (hashVal === 0) {
|
||||||
if (val !== currentStoreProps[key]) {
|
if (val !== currentStoreProps[key]) {
|
||||||
options.didChange = true;
|
options.didChange = true;
|
||||||
@@ -226,20 +213,25 @@ export function connect<E extends EnvWithStore, P, S>(
|
|||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const Result = class extends Comp {
|
static mapStoreToProps(storeState, ownProps, getters) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
constructor(parent, props?: any) {
|
constructor(parent, props?: any) {
|
||||||
const env = parent instanceof Component ? parent.env : parent;
|
super(parent, props);
|
||||||
const store = getStore(env);
|
const store = this.getStore(this.env);
|
||||||
const ownProps = Object.assign({}, props || {});
|
const ownProps = Object.assign({}, props || {});
|
||||||
const storeProps = mapStoreToProps(store.state, ownProps, store.getters);
|
const storeProps = (<any>this.constructor).mapStoreToProps(
|
||||||
|
store.state,
|
||||||
|
ownProps,
|
||||||
|
store.getters
|
||||||
|
);
|
||||||
const mergedProps = Object.assign({}, props || {}, storeProps);
|
const mergedProps = Object.assign({}, props || {}, storeProps);
|
||||||
super(parent, mergedProps);
|
this.props = mergedProps;
|
||||||
(<any>this.__owl__).ownProps = ownProps;
|
(<any>this.__owl__).ownProps = ownProps;
|
||||||
(<any>this.__owl__).currentStoreProps = storeProps;
|
(<any>this.__owl__).currentStoreProps = storeProps;
|
||||||
(<any>this.__owl__).store = store;
|
(<any>this.__owl__).store = store;
|
||||||
(<any>this.__owl__).storeHash = (<HashFunction>hashFunction)(
|
(<any>this.__owl__).storeHash = this.hashFunction(
|
||||||
{
|
{
|
||||||
state: store.state,
|
state: store.state,
|
||||||
storeProps: storeProps,
|
storeProps: storeProps,
|
||||||
@@ -270,7 +262,7 @@ export function connect<E extends EnvWithStore, P, S>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ownProps = (<any>this.__owl__).ownProps;
|
const ownProps = (<any>this.__owl__).ownProps;
|
||||||
const storeProps = mapStoreToProps(
|
const storeProps = (<any>this.constructor).mapStoreToProps(
|
||||||
(<any>this.__owl__).store.state,
|
(<any>this.__owl__).store.state,
|
||||||
ownProps,
|
ownProps,
|
||||||
(<any>this.__owl__).store.getters
|
(<any>this.__owl__).store.getters
|
||||||
@@ -278,7 +270,7 @@ export function connect<E extends EnvWithStore, P, S>(
|
|||||||
const options: any = {
|
const options: any = {
|
||||||
currentStoreProps: (<any>this.__owl__).currentStoreProps
|
currentStoreProps: (<any>this.__owl__).currentStoreProps
|
||||||
};
|
};
|
||||||
const storeHash = (<HashFunction>hashFunction)(
|
const storeHash = this.hashFunction(
|
||||||
{
|
{
|
||||||
state: (<any>this.__owl__).store.state,
|
state: (<any>this.__owl__).store.state,
|
||||||
storeProps: storeProps,
|
storeProps: storeProps,
|
||||||
@@ -301,7 +293,7 @@ export function connect<E extends EnvWithStore, P, S>(
|
|||||||
const __owl__ = <any>this.__owl__;
|
const __owl__ = <any>this.__owl__;
|
||||||
__owl__.currentUpdateId = __owl__.store._updateId;
|
__owl__.currentUpdateId = __owl__.store._updateId;
|
||||||
if (__owl__.ownProps !== nextProps) {
|
if (__owl__.ownProps !== nextProps) {
|
||||||
__owl__.currentStoreProps = mapStoreToProps(
|
__owl__.currentStoreProps = (<any>this.constructor).mapStoreToProps(
|
||||||
__owl__.store.state,
|
__owl__.store.state,
|
||||||
nextProps,
|
nextProps,
|
||||||
__owl__.store.getters
|
__owl__.store.getters
|
||||||
@@ -311,13 +303,4 @@ export function connect<E extends EnvWithStore, P, S>(
|
|||||||
const mergedProps = Object.assign({}, nextProps, __owl__.currentStoreProps);
|
const mergedProps = Object.assign({}, nextProps, __owl__.currentStoreProps);
|
||||||
return super.__updateProps(mergedProps, forceUpdate, patchQueue);
|
return super.__updateProps(mergedProps, forceUpdate, patchQueue);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// we assign here a unique name to the resulting anonymous class.
|
|
||||||
// this is necessary for Owl to be able to properly deduce templates.
|
|
||||||
// Otherwise, all connected components would have the same name, and then
|
|
||||||
// each component after the first will necessarily have the same template.
|
|
||||||
let name = `Connected${Comp.name}`;
|
|
||||||
Object.defineProperty(Result, "name", { value: name });
|
|
||||||
return Result;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ exports[`animations t-transition combined with component 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -27,8 +28,8 @@ exports[`animations t-transition combined with component 1`] = `
|
|||||||
let componentKey4 = \`Child\`;
|
let componentKey4 = \`Child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
||||||
w4.destroy();
|
w4.destroy();
|
||||||
@@ -48,6 +49,7 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
@@ -55,7 +57,7 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
|||||||
if (context['state'].display) {
|
if (context['state'].display) {
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -71,8 +73,8 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
|||||||
let componentKey4 = \`Child\`;
|
let componentKey4 = \`Child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
||||||
w4.destroy();
|
w4.destroy();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ exports[`async rendering delayed component with t-asyncroot directive 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
@@ -24,7 +25,7 @@ exports[`async rendering delayed component with t-asyncroot directive 1`] = `
|
|||||||
c1.push(vn5);
|
c1.push(vn5);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def7;
|
let def7;
|
||||||
let w8 = 8 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[8]] : false;
|
let w8 = 8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[8]] : false;
|
||||||
let _6_index = c5.length;
|
let _6_index = c5.length;
|
||||||
c5.push(null);
|
c5.push(null);
|
||||||
let props8 = {val:context['state'].val};
|
let props8 = {val:context['state'].val};
|
||||||
@@ -40,8 +41,8 @@ exports[`async rendering delayed component with t-asyncroot directive 1`] = `
|
|||||||
let componentKey8 = \`Child\`;
|
let componentKey8 = \`Child\`;
|
||||||
let W8 = context.components && context.components[componentKey8] || QWeb.components[componentKey8];
|
let W8 = context.components && context.components[componentKey8] || QWeb.components[componentKey8];
|
||||||
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
||||||
w8 = new W8(owner, props8);
|
w8 = new W8(parent, props8);
|
||||||
context.__owl__.cmap[8] = w8.__owl__.id;
|
parent.__owl__.cmap[8] = w8.__owl__.id;
|
||||||
def7 = w8.__prepare();
|
def7 = w8.__prepare();
|
||||||
def7 = def7.then(vnode=>{let pvnode=h(vnode.sel, {key: 8, hook: {insert(vn) {let nvn=w8.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}});c5[_6_index]=pvnode;w8.__owl__.pvnode = pvnode;});
|
def7 = def7.then(vnode=>{let pvnode=h(vnode.sel, {key: 8, hook: {insert(vn) {let nvn=w8.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}});c5[_6_index]=pvnode;w8.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -51,7 +52,7 @@ exports[`async rendering delayed component with t-asyncroot directive 1`] = `
|
|||||||
extra.promises.push(def7);
|
extra.promises.push(def7);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def10;
|
let def10;
|
||||||
let w11 = 11 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[11]] : false;
|
let w11 = 11 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[11]] : false;
|
||||||
let _9_index = c5.length;
|
let _9_index = c5.length;
|
||||||
const patchQueue11 = [];
|
const patchQueue11 = [];
|
||||||
c5.push(w11 && w11.__owl__.pvnode || null);
|
c5.push(w11 && w11.__owl__.pvnode || null);
|
||||||
@@ -68,8 +69,8 @@ exports[`async rendering delayed component with t-asyncroot directive 1`] = `
|
|||||||
let componentKey11 = \`AsyncChild\`;
|
let componentKey11 = \`AsyncChild\`;
|
||||||
let W11 = context.components && context.components[componentKey11] || QWeb.components[componentKey11];
|
let W11 = context.components && context.components[componentKey11] || QWeb.components[componentKey11];
|
||||||
if (!W11) {throw new Error('Cannot find the definition of component \\"' + componentKey11 + '\\"')}
|
if (!W11) {throw new Error('Cannot find the definition of component \\"' + componentKey11 + '\\"')}
|
||||||
w11 = new W11(owner, props11);
|
w11 = new W11(parent, props11);
|
||||||
context.__owl__.cmap[11] = w11.__owl__.id;
|
parent.__owl__.cmap[11] = w11.__owl__.id;
|
||||||
def10 = w11.__prepare();
|
def10 = w11.__prepare();
|
||||||
def10 = def10.then(vnode=>{let pvnode=h(vnode.sel, {key: 11, hook: {insert(vn) {let nvn=w11.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w11.destroy();}}});c5[_9_index]=pvnode;w11.__owl__.pvnode = pvnode;});
|
def10 = def10.then(vnode=>{let pvnode=h(vnode.sel, {key: 11, hook: {insert(vn) {let nvn=w11.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w11.destroy();}}});c5[_9_index]=pvnode;w11.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -86,6 +87,7 @@ exports[`async rendering fast component with t-asyncroot directive 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
@@ -105,7 +107,7 @@ exports[`async rendering fast component with t-asyncroot directive 1`] = `
|
|||||||
c1.push(vn5);
|
c1.push(vn5);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def7;
|
let def7;
|
||||||
let w8 = 8 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[8]] : false;
|
let w8 = 8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[8]] : false;
|
||||||
let _6_index = c5.length;
|
let _6_index = c5.length;
|
||||||
const patchQueue8 = [];
|
const patchQueue8 = [];
|
||||||
c5.push(w8 && w8.__owl__.pvnode || null);
|
c5.push(w8 && w8.__owl__.pvnode || null);
|
||||||
@@ -122,8 +124,8 @@ exports[`async rendering fast component with t-asyncroot directive 1`] = `
|
|||||||
let componentKey8 = \`Child\`;
|
let componentKey8 = \`Child\`;
|
||||||
let W8 = context.components && context.components[componentKey8] || QWeb.components[componentKey8];
|
let W8 = context.components && context.components[componentKey8] || QWeb.components[componentKey8];
|
||||||
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
||||||
w8 = new W8(owner, props8);
|
w8 = new W8(parent, props8);
|
||||||
context.__owl__.cmap[8] = w8.__owl__.id;
|
parent.__owl__.cmap[8] = w8.__owl__.id;
|
||||||
def7 = w8.__prepare();
|
def7 = w8.__prepare();
|
||||||
def7 = def7.then(vnode=>{let pvnode=h(vnode.sel, {key: 8, hook: {insert(vn) {let nvn=w8.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}});c5[_6_index]=pvnode;w8.__owl__.pvnode = pvnode;});
|
def7 = def7.then(vnode=>{let pvnode=h(vnode.sel, {key: 8, hook: {insert(vn) {let nvn=w8.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}});c5[_6_index]=pvnode;w8.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -133,7 +135,7 @@ exports[`async rendering fast component with t-asyncroot directive 1`] = `
|
|||||||
def7.then(w8.__applyPatchQueue.bind(w8, patchQueue8));
|
def7.then(w8.__applyPatchQueue.bind(w8, patchQueue8));
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def10;
|
let def10;
|
||||||
let w11 = 11 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[11]] : false;
|
let w11 = 11 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[11]] : false;
|
||||||
let _9_index = c5.length;
|
let _9_index = c5.length;
|
||||||
c5.push(null);
|
c5.push(null);
|
||||||
let props11 = {val:context['state'].val};
|
let props11 = {val:context['state'].val};
|
||||||
@@ -149,8 +151,8 @@ exports[`async rendering fast component with t-asyncroot directive 1`] = `
|
|||||||
let componentKey11 = \`AsyncChild\`;
|
let componentKey11 = \`AsyncChild\`;
|
||||||
let W11 = context.components && context.components[componentKey11] || QWeb.components[componentKey11];
|
let W11 = context.components && context.components[componentKey11] || QWeb.components[componentKey11];
|
||||||
if (!W11) {throw new Error('Cannot find the definition of component \\"' + componentKey11 + '\\"')}
|
if (!W11) {throw new Error('Cannot find the definition of component \\"' + componentKey11 + '\\"')}
|
||||||
w11 = new W11(owner, props11);
|
w11 = new W11(parent, props11);
|
||||||
context.__owl__.cmap[11] = w11.__owl__.id;
|
parent.__owl__.cmap[11] = w11.__owl__.id;
|
||||||
def10 = w11.__prepare();
|
def10 = w11.__prepare();
|
||||||
def10 = def10.then(vnode=>{let pvnode=h(vnode.sel, {key: 11, hook: {insert(vn) {let nvn=w11.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w11.destroy();}}});c5[_9_index]=pvnode;w11.__owl__.pvnode = pvnode;});
|
def10 = def10.then(vnode=>{let pvnode=h(vnode.sel, {key: 11, hook: {insert(vn) {let nvn=w11.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w11.destroy();}}});c5[_9_index]=pvnode;w11.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -167,6 +169,7 @@ exports[`async rendering t-component with t-asyncroot directive: mixed re-render
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
@@ -186,7 +189,7 @@ exports[`async rendering t-component with t-asyncroot directive: mixed re-render
|
|||||||
c1.push(vn5);
|
c1.push(vn5);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def7;
|
let def7;
|
||||||
let w8 = 8 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[8]] : false;
|
let w8 = 8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[8]] : false;
|
||||||
let _6_index = c5.length;
|
let _6_index = c5.length;
|
||||||
c5.push(null);
|
c5.push(null);
|
||||||
let props8 = {val:context['state'].val};
|
let props8 = {val:context['state'].val};
|
||||||
@@ -202,8 +205,8 @@ exports[`async rendering t-component with t-asyncroot directive: mixed re-render
|
|||||||
let componentKey8 = \`Child\`;
|
let componentKey8 = \`Child\`;
|
||||||
let W8 = context.components && context.components[componentKey8] || QWeb.components[componentKey8];
|
let W8 = context.components && context.components[componentKey8] || QWeb.components[componentKey8];
|
||||||
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
||||||
w8 = new W8(owner, props8);
|
w8 = new W8(parent, props8);
|
||||||
context.__owl__.cmap[8] = w8.__owl__.id;
|
parent.__owl__.cmap[8] = w8.__owl__.id;
|
||||||
def7 = w8.__prepare();
|
def7 = w8.__prepare();
|
||||||
def7 = def7.then(vnode=>{let pvnode=h(vnode.sel, {key: 8, hook: {insert(vn) {let nvn=w8.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}});c5[_6_index]=pvnode;w8.__owl__.pvnode = pvnode;});
|
def7 = def7.then(vnode=>{let pvnode=h(vnode.sel, {key: 8, hook: {insert(vn) {let nvn=w8.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}});c5[_6_index]=pvnode;w8.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -213,7 +216,7 @@ exports[`async rendering t-component with t-asyncroot directive: mixed re-render
|
|||||||
extra.promises.push(def7);
|
extra.promises.push(def7);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def10;
|
let def10;
|
||||||
let w11 = 11 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[11]] : false;
|
let w11 = 11 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[11]] : false;
|
||||||
let _9_index = c5.length;
|
let _9_index = c5.length;
|
||||||
const patchQueue11 = [];
|
const patchQueue11 = [];
|
||||||
c5.push(w11 && w11.__owl__.pvnode || null);
|
c5.push(w11 && w11.__owl__.pvnode || null);
|
||||||
@@ -230,8 +233,8 @@ exports[`async rendering t-component with t-asyncroot directive: mixed re-render
|
|||||||
let componentKey11 = \`AsyncChild\`;
|
let componentKey11 = \`AsyncChild\`;
|
||||||
let W11 = context.components && context.components[componentKey11] || QWeb.components[componentKey11];
|
let W11 = context.components && context.components[componentKey11] || QWeb.components[componentKey11];
|
||||||
if (!W11) {throw new Error('Cannot find the definition of component \\"' + componentKey11 + '\\"')}
|
if (!W11) {throw new Error('Cannot find the definition of component \\"' + componentKey11 + '\\"')}
|
||||||
w11 = new W11(owner, props11);
|
w11 = new W11(parent, props11);
|
||||||
context.__owl__.cmap[11] = w11.__owl__.id;
|
parent.__owl__.cmap[11] = w11.__owl__.id;
|
||||||
def10 = w11.__prepare();
|
def10 = w11.__prepare();
|
||||||
def10 = def10.then(vnode=>{let pvnode=h(vnode.sel, {key: 11, hook: {insert(vn) {let nvn=w11.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w11.destroy();}}});c5[_9_index]=pvnode;w11.__owl__.pvnode = pvnode;});
|
def10 = def10.then(vnode=>{let pvnode=h(vnode.sel, {key: 11, hook: {insert(vn) {let nvn=w11.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w11.destroy();}}});c5[_9_index]=pvnode;w11.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -248,6 +251,7 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
@@ -255,7 +259,7 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop
|
|||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
const _5 = context['state'].style;
|
const _5 = context['state'].style;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -271,8 +275,8 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.style = _5;}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.style = _5;}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -289,6 +293,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
@@ -298,7 +303,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
|||||||
const ref5 = \`child\`;
|
const ref5 = \`child\`;
|
||||||
let _6 = {'a':true};
|
let _6 = {'a':true};
|
||||||
Object.assign(_6, {b:context['state'].b})
|
Object.assign(_6, {b:context['state'].b})
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -314,8 +319,8 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
|||||||
let componentKey4 = \`Child\`;
|
let componentKey4 = \`Child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;context.refs[ref5] = w4;},remove() {},destroy(vn) {w4.destroy();delete context.refs[ref5];}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;context.refs[ref5] = w4;},remove() {},destroy(vn) {w4.destroy();delete context.refs[ref5];}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -345,6 +350,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
@@ -353,8 +359,8 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
|||||||
let def3;
|
let def3;
|
||||||
const ref5 = \`child\`;
|
const ref5 = \`child\`;
|
||||||
let _6 = {'a':true};
|
let _6 = {'a':true};
|
||||||
Object.assign(_6, this.utils.toObj(context['state'].b?'b':''))
|
Object.assign(_6, utils.toObj(context['state'].b?'b':''))
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -370,8 +376,8 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
|||||||
let componentKey4 = \`Child\`;
|
let componentKey4 = \`Child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;context.refs[ref5] = w4;},remove() {},destroy(vn) {w4.destroy();delete context.refs[ref5];}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;context.refs[ref5] = w4;},remove() {},destroy(vn) {w4.destroy();delete context.refs[ref5];}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -401,6 +407,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
@@ -408,7 +415,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
|||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let _5 = {a:context['state'].a,b:context['state'].b};
|
let _5 = {a:context['state'].a,b:context['state'].b};
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -424,8 +431,8 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -443,6 +450,7 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
context = Object.create(context);
|
context = Object.create(context);
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
@@ -465,7 +473,7 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
|
|||||||
//COMPONENT
|
//COMPONENT
|
||||||
let key8 = context['number'];
|
let key8 = context['number'];
|
||||||
let def6;
|
let def6;
|
||||||
let w7 = key8 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[key8]] : false;
|
let w7 = key8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[key8]] : false;
|
||||||
let _5_index = c1.length;
|
let _5_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props7 = {};
|
let props7 = {};
|
||||||
@@ -481,8 +489,8 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
|
|||||||
let componentKey7 = \`ChildWidget\`;
|
let componentKey7 = \`ChildWidget\`;
|
||||||
let W7 = context.components && context.components[componentKey7] || QWeb.components[componentKey7];
|
let W7 = context.components && context.components[componentKey7] || QWeb.components[componentKey7];
|
||||||
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
|
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
|
||||||
w7 = new W7(owner, props7);
|
w7 = new W7(parent, props7);
|
||||||
context.__owl__.cmap[key8] = w7.__owl__.id;
|
parent.__owl__.cmap[key8] = w7.__owl__.id;
|
||||||
def6 = w7.__prepare();
|
def6 = w7.__prepare();
|
||||||
def6 = def6.then(vnode=>{let pvnode=h(vnode.sel, {key: key8, hook: {insert(vn) {let nvn=w7.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});c1[_5_index]=pvnode;w7.__owl__.pvnode = pvnode;});
|
def6 = def6.then(vnode=>{let pvnode=h(vnode.sel, {key: key8, hook: {insert(vn) {let nvn=w7.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});c1[_5_index]=pvnode;w7.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -500,13 +508,14 @@ exports[`composition t-component with dynamic value 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -522,8 +531,8 @@ exports[`composition t-component with dynamic value 1`] = `
|
|||||||
let componentKey4 = (context['state'].widget);
|
let componentKey4 = (context['state'].widget);
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -540,13 +549,14 @@ exports[`composition t-component with dynamic value 2 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -562,8 +572,8 @@ exports[`composition t-component with dynamic value 2 1`] = `
|
|||||||
let componentKey4 = \`Widget\${context['state'].widget}\`;
|
let componentKey4 = \`Widget\${context['state'].widget}\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -580,13 +590,14 @@ exports[`other directives with t-component t-on with handler bound to argument 1
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -602,8 +613,8 @@ exports[`other directives with t-component t-on with handler bound to argument 1
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, 3));}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, 3));}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -620,13 +631,14 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -642,8 +654,8 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, {}));}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, {}));}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -660,13 +672,14 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -682,8 +695,8 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, {}));}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, {}));}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -700,13 +713,14 @@ exports[`other directives with t-component t-on with handler bound to object 1`]
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -722,8 +736,8 @@ exports[`other directives with t-component t-on with handler bound to object 1`]
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, {val:3}));}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, {val:3}));}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -740,13 +754,14 @@ exports[`other directives with t-component t-on with prevent and self modifiers
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -762,8 +777,8 @@ exports[`other directives with t-component t-on with prevent and self modifiers
|
|||||||
let componentKey4 = \`Child\`;
|
let componentKey4 = \`Child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {e.preventDefault();if (e.target !== vn.elm) {return}owner['onEv'].call(owner, e);});}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {e.preventDefault();if (e.target !== vn.elm) {return}owner['onEv'].call(owner, e);});}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -780,13 +795,14 @@ exports[`other directives with t-component t-on with self and prevent modifiers
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -802,8 +818,8 @@ exports[`other directives with t-component t-on with self and prevent modifiers
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (e.target !== vn.elm) {return}e.preventDefault();owner['onEv'].call(owner, e);});}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (e.target !== vn.elm) {return}e.preventDefault();owner['onEv'].call(owner, e);});}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -820,13 +836,14 @@ exports[`other directives with t-component t-on with self modifier 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -842,8 +859,8 @@ exports[`other directives with t-component t-on with self modifier 1`] = `
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', owner['onEv1'].bind(owner));vn.elm.addEventListener('ev-2', function (e) {if (e.target !== vn.elm) {return}owner['onEv2'].call(owner, e);});}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', owner['onEv1'].bind(owner));vn.elm.addEventListener('ev-2', function (e) {if (e.target !== vn.elm) {return}owner['onEv2'].call(owner, e);});}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -860,13 +877,14 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -882,8 +900,8 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {e.stopPropagation();owner['onEv1'].call(owner, e);});vn.elm.addEventListener('ev-2', function (e) {e.preventDefault();owner['onEv2'].call(owner, e);});vn.elm.addEventListener('ev-3', function (e) {e.stopPropagation();e.preventDefault();owner['onEv3'].call(owner, e);});}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {e.stopPropagation();owner['onEv1'].call(owner, e);});vn.elm.addEventListener('ev-2', function (e) {e.preventDefault();owner['onEv2'].call(owner, e);});vn.elm.addEventListener('ev-3', function (e) {e.stopPropagation();e.preventDefault();owner['onEv3'].call(owner, e);});}};let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -900,6 +918,7 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
@@ -907,7 +926,7 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
|
|||||||
//COMPONENT
|
//COMPONENT
|
||||||
let key5 = 'somestring';
|
let key5 = 'somestring';
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = key5 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[key5]] : false;
|
let w4 = key5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[key5]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {flag:context['state'].flag};
|
let props4 = {flag:context['state'].flag};
|
||||||
@@ -923,8 +942,8 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
|
|||||||
let componentKey4 = \`child\`;
|
let componentKey4 = \`child\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[key5] = w4.__owl__.id;
|
parent.__owl__.cmap[key5] = w4.__owl__.id;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: key5, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: key5, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -941,6 +960,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
context = Object.create(context);
|
context = Object.create(context);
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
@@ -964,7 +984,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
|||||||
let key8 = context['item'];
|
let key8 = context['item'];
|
||||||
let def6;
|
let def6;
|
||||||
let arg9 = context['item'];
|
let arg9 = context['item'];
|
||||||
let w7 = key8 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[key8]] : false;
|
let w7 = key8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[key8]] : false;
|
||||||
let _5_index = c1.length;
|
let _5_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props7 = {};
|
let props7 = {};
|
||||||
@@ -980,8 +1000,8 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
|||||||
let componentKey7 = \`Child\`;
|
let componentKey7 = \`Child\`;
|
||||||
let W7 = context.components && context.components[componentKey7] || QWeb.components[componentKey7];
|
let W7 = context.components && context.components[componentKey7] || QWeb.components[componentKey7];
|
||||||
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
|
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
|
||||||
w7 = new W7(owner, props7);
|
w7 = new W7(parent, props7);
|
||||||
context.__owl__.cmap[key8] = w7.__owl__.id;
|
parent.__owl__.cmap[key8] = w7.__owl__.id;
|
||||||
def6 = w7.__prepare();
|
def6 = w7.__prepare();
|
||||||
def6 = def6.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, arg9));}};let pvnode=h(vnode.sel, {key: key8, hook: {insert(vn) {let nvn=w7.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});c1[_5_index]=pvnode;w7.__owl__.pvnode = pvnode;});
|
def6 = def6.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, arg9));}};let pvnode=h(vnode.sel, {key: key8, hook: {insert(vn) {let nvn=w7.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});c1[_5_index]=pvnode;w7.__owl__.pvnode = pvnode;});
|
||||||
} else {
|
} else {
|
||||||
@@ -1146,13 +1166,14 @@ exports[`t-slot directive can define and call slots 1`] = `
|
|||||||
) {
|
) {
|
||||||
let utils = this.utils;
|
let utils = this.utils;
|
||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
let owner = context;
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
//COMPONENT
|
//COMPONENT
|
||||||
let def3;
|
let def3;
|
||||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
let _2_index = c1.length;
|
let _2_index = c1.length;
|
||||||
c1.push(null);
|
c1.push(null);
|
||||||
let props4 = {};
|
let props4 = {};
|
||||||
@@ -1168,8 +1189,8 @@ exports[`t-slot directive can define and call slots 1`] = `
|
|||||||
let componentKey4 = \`Dialog\`;
|
let componentKey4 = \`Dialog\`;
|
||||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
w4 = new W4(owner, props4);
|
w4 = new W4(parent, props4);
|
||||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
w4.__owl__.slotId = 1;
|
w4.__owl__.slotId = 1;
|
||||||
def3 = w4.__prepare();
|
def3 = w4.__prepare();
|
||||||
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
@@ -1185,6 +1206,7 @@ exports[`t-slot directive can define and call slots 1`] = `
|
|||||||
exports[`t-slot directive can define and call slots 2`] = `
|
exports[`t-slot directive can define and call slots 2`] = `
|
||||||
"function anonymous(context,extra
|
"function anonymous(context,extra
|
||||||
) {
|
) {
|
||||||
|
let owner = context;
|
||||||
var h = this.utils.h;
|
var h = this.utils.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
@@ -1193,15 +1215,342 @@ exports[`t-slot directive can define and call slots 2`] = `
|
|||||||
c1.push(vn2);
|
c1.push(vn2);
|
||||||
const slot3 = this.slots[context.__owl__.slotId + '_' + 'header'];
|
const slot3 = this.slots[context.__owl__.slotId + '_' + 'header'];
|
||||||
if (slot3) {
|
if (slot3) {
|
||||||
slot3(context.__owl__.parent, Object.assign({}, extra, {parentNode: c2}));
|
slot3(context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||||
}
|
}
|
||||||
let c4 = [], p4 = {key:4};
|
let c4 = [], p4 = {key:4};
|
||||||
var vn4 = h('div', p4, c4);
|
var vn4 = h('div', p4, c4);
|
||||||
c1.push(vn4);
|
c1.push(vn4);
|
||||||
const slot5 = this.slots[context.__owl__.slotId + '_' + 'footer'];
|
const slot5 = this.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||||
if (slot5) {
|
if (slot5) {
|
||||||
slot5(context.__owl__.parent, Object.assign({}, extra, {parentNode: c4}));
|
slot5(context.__owl__.parent, Object.assign({}, extra, {parentNode: c4, vars: extra.vars, parent: owner}));
|
||||||
}
|
}
|
||||||
return vn1;
|
return vn1;
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
let owner = context;
|
||||||
|
var h = this.utils.h;
|
||||||
|
var _1 = context['props'].to;
|
||||||
|
let c2 = [], p2 = {key:2,attrs:{href: _1}};
|
||||||
|
var vn2 = h('a', p2, c2);
|
||||||
|
const slot3 = this.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
|
if (slot3) {
|
||||||
|
slot3(context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||||
|
}
|
||||||
|
return vn2;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
let utils = this.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let owner = context;
|
||||||
|
context = Object.create(context);
|
||||||
|
const scope = Object.create(null);
|
||||||
|
var h = this.utils.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('div', p1, c1);
|
||||||
|
let c2 = [], p2 = {key:2};
|
||||||
|
var vn2 = h('u', p2, c2);
|
||||||
|
c1.push(vn2);
|
||||||
|
var _3 = context['state'].users;
|
||||||
|
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
||||||
|
var _4 = _5 = _3;
|
||||||
|
if (!(_3 instanceof Array)) {
|
||||||
|
_4 = Object.keys(_3);
|
||||||
|
_5 = Object.values(_3);
|
||||||
|
}
|
||||||
|
var _length4 = _4.length;
|
||||||
|
for (let i = 0; i < _length4; i++) {
|
||||||
|
context.user_first = i === 0;
|
||||||
|
scope.user_first = context.user_first;
|
||||||
|
context.user_last = i === _length4 - 1;
|
||||||
|
scope.user_last = context.user_last;
|
||||||
|
context.user_index = i;
|
||||||
|
scope.user_index = context.user_index;
|
||||||
|
context.user = _4[i];
|
||||||
|
scope.user = context.user;
|
||||||
|
context.user_value = _5[i];
|
||||||
|
scope.user_value = context.user_value;
|
||||||
|
let c6 = [], p6 = {key:context['user'].id};
|
||||||
|
var vn6 = h('li', p6, c6);
|
||||||
|
c2.push(vn6);
|
||||||
|
//COMPONENT
|
||||||
|
let def8;
|
||||||
|
let w9 = String(-9 - i) in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[String(-9 - i)]] : false;
|
||||||
|
let _7_index = c6.length;
|
||||||
|
c6.push(null);
|
||||||
|
let props9 = {to:'/user/'+context['user'].id};
|
||||||
|
if (w9 && w9.__owl__.renderPromise && !w9.__owl__.vnode) {
|
||||||
|
if (utils.shallowEqual(props9, w9.__owl__.renderProps)) {
|
||||||
|
def8 = w9.__owl__.renderPromise;
|
||||||
|
} else {
|
||||||
|
w9.destroy();
|
||||||
|
w9 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!w9) {
|
||||||
|
let componentKey9 = \`Link\`;
|
||||||
|
let W9 = context.components && context.components[componentKey9] || QWeb.components[componentKey9];
|
||||||
|
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')}
|
||||||
|
w9 = new W9(parent, props9);
|
||||||
|
parent.__owl__.cmap[String(-9 - i)] = w9.__owl__.id;
|
||||||
|
w9.__owl__.slotId = 1;
|
||||||
|
def8 = w9.__prepare(Object.assign({}, scope));
|
||||||
|
def8 = def8.then(vnode=>{let pvnode=h(vnode.sel, {key: String(-9 - i), hook: {insert(vn) {let nvn=w9.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w9.destroy();}}});c6[_7_index]=pvnode;w9.__owl__.pvnode = pvnode;});
|
||||||
|
} else {
|
||||||
|
def8 = def8 || w9.__updateProps(props9, extra.forceUpdate, extra.patchQueue, Object.assign({}, scope));
|
||||||
|
def8 = def8.then(()=>{if (w9.__owl__.isDestroyed) {return};let pvnode=w9.__owl__.pvnode;c6[_7_index]=pvnode;});
|
||||||
|
}
|
||||||
|
extra.promises.push(def8);
|
||||||
|
}
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
let owner = context;
|
||||||
|
var h = this.utils.h;
|
||||||
|
var _1 = context['props'].to;
|
||||||
|
let c2 = [], p2 = {key:2,attrs:{href: _1}};
|
||||||
|
var vn2 = h('a', p2, c2);
|
||||||
|
const slot3 = this.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
|
if (slot3) {
|
||||||
|
slot3(context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||||
|
}
|
||||||
|
return vn2;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
let utils = this.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let owner = context;
|
||||||
|
context = Object.create(context);
|
||||||
|
const scope = Object.create(null);
|
||||||
|
var h = this.utils.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('div', p1, c1);
|
||||||
|
let c2 = [], p2 = {key:2};
|
||||||
|
var vn2 = h('u', p2, c2);
|
||||||
|
c1.push(vn2);
|
||||||
|
var _3 = context['state'].users;
|
||||||
|
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
||||||
|
var _4 = _5 = _3;
|
||||||
|
if (!(_3 instanceof Array)) {
|
||||||
|
_4 = Object.keys(_3);
|
||||||
|
_5 = Object.values(_3);
|
||||||
|
}
|
||||||
|
var _length4 = _4.length;
|
||||||
|
for (let i = 0; i < _length4; i++) {
|
||||||
|
context.user_first = i === 0;
|
||||||
|
scope.user_first = context.user_first;
|
||||||
|
context.user_last = i === _length4 - 1;
|
||||||
|
scope.user_last = context.user_last;
|
||||||
|
context.user_index = i;
|
||||||
|
scope.user_index = context.user_index;
|
||||||
|
context.user = _4[i];
|
||||||
|
scope.user = context.user;
|
||||||
|
context.user_value = _5[i];
|
||||||
|
scope.user_value = context.user_value;
|
||||||
|
let c6 = [], p6 = {key:context['user'].id};
|
||||||
|
var vn6 = h('li', p6, c6);
|
||||||
|
c2.push(vn6);
|
||||||
|
var _7 = 'User '+context['user'].name;
|
||||||
|
//COMPONENT
|
||||||
|
let def9;
|
||||||
|
let w10 = String(-10 - i) in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[String(-10 - i)]] : false;
|
||||||
|
let _8_index = c6.length;
|
||||||
|
c6.push(null);
|
||||||
|
let props10 = {to:'/user/'+context['user'].id};
|
||||||
|
if (w10 && w10.__owl__.renderPromise && !w10.__owl__.vnode) {
|
||||||
|
if (utils.shallowEqual(props10, w10.__owl__.renderProps)) {
|
||||||
|
def9 = w10.__owl__.renderPromise;
|
||||||
|
} else {
|
||||||
|
w10.destroy();
|
||||||
|
w10 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!w10) {
|
||||||
|
let componentKey10 = \`Link\`;
|
||||||
|
let W10 = context.components && context.components[componentKey10] || QWeb.components[componentKey10];
|
||||||
|
if (!W10) {throw new Error('Cannot find the definition of component \\"' + componentKey10 + '\\"')}
|
||||||
|
w10 = new W10(parent, props10);
|
||||||
|
parent.__owl__.cmap[String(-10 - i)] = w10.__owl__.id;
|
||||||
|
w10.__owl__.slotId = 1;
|
||||||
|
def9 = w10.__prepare(Object.assign({}, scope), {_7});
|
||||||
|
def9 = def9.then(vnode=>{let pvnode=h(vnode.sel, {key: String(-10 - i), hook: {insert(vn) {let nvn=w10.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w10.destroy();}}});c6[_8_index]=pvnode;w10.__owl__.pvnode = pvnode;});
|
||||||
|
} else {
|
||||||
|
def9 = def9 || w10.__updateProps(props10, extra.forceUpdate, extra.patchQueue, Object.assign({}, scope), {_7});
|
||||||
|
def9 = def9.then(()=>{if (w10.__owl__.isDestroyed) {return};let pvnode=w10.__owl__.pvnode;c6[_8_index]=pvnode;});
|
||||||
|
}
|
||||||
|
extra.promises.push(def9);
|
||||||
|
}
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
let utils = this.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let owner = context;
|
||||||
|
var h = this.utils.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('div', p1, c1);
|
||||||
|
var _2 = 'User '+context['state'].user.name;
|
||||||
|
//COMPONENT
|
||||||
|
let def4;
|
||||||
|
let w5 = 5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[5]] : false;
|
||||||
|
let _3_index = c1.length;
|
||||||
|
c1.push(null);
|
||||||
|
let props5 = {to:'/user/'+context['state'].user.id};
|
||||||
|
if (w5 && w5.__owl__.renderPromise && !w5.__owl__.vnode) {
|
||||||
|
if (utils.shallowEqual(props5, w5.__owl__.renderProps)) {
|
||||||
|
def4 = w5.__owl__.renderPromise;
|
||||||
|
} else {
|
||||||
|
w5.destroy();
|
||||||
|
w5 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!w5) {
|
||||||
|
let componentKey5 = \`Link\`;
|
||||||
|
let W5 = context.components && context.components[componentKey5] || QWeb.components[componentKey5];
|
||||||
|
if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')}
|
||||||
|
w5 = new W5(parent, props5);
|
||||||
|
parent.__owl__.cmap[5] = w5.__owl__.id;
|
||||||
|
w5.__owl__.slotId = 1;
|
||||||
|
def4 = w5.__prepare({}, {_2});
|
||||||
|
def4 = def4.then(vnode=>{let pvnode=h(vnode.sel, {key: 5, hook: {insert(vn) {let nvn=w5.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w5.destroy();}}});c1[_3_index]=pvnode;w5.__owl__.pvnode = pvnode;});
|
||||||
|
} else {
|
||||||
|
def4 = def4 || w5.__updateProps(props5, extra.forceUpdate, extra.patchQueue, {}, {_2});
|
||||||
|
def4 = def4.then(()=>{if (w5.__owl__.isDestroyed) {return};let pvnode=w5.__owl__.pvnode;c1[_3_index]=pvnode;});
|
||||||
|
}
|
||||||
|
extra.promises.push(def4);
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`top level sub widgets basic use 1`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
let utils = this.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let owner = context;
|
||||||
|
let result;
|
||||||
|
var h = this.utils.h;
|
||||||
|
//COMPONENT
|
||||||
|
let def2;
|
||||||
|
let w3 = 3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[3]] : false;
|
||||||
|
let vn4 = {};
|
||||||
|
result = vn4;
|
||||||
|
let props3 = {p:1};
|
||||||
|
if (w3 && w3.__owl__.renderPromise && !w3.__owl__.vnode) {
|
||||||
|
if (utils.shallowEqual(props3, w3.__owl__.renderProps)) {
|
||||||
|
def2 = w3.__owl__.renderPromise;
|
||||||
|
} else {
|
||||||
|
w3.destroy();
|
||||||
|
w3 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!w3) {
|
||||||
|
let componentKey3 = \`Child\`;
|
||||||
|
let W3 = context.components && context.components[componentKey3] || QWeb.components[componentKey3];
|
||||||
|
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||||
|
w3 = new W3(parent, props3);
|
||||||
|
parent.__owl__.cmap[3] = w3.__owl__.id;
|
||||||
|
def2 = w3.__prepare();
|
||||||
|
def2 = def2.then(vnode=>{let pvnode=h(vnode.sel, {key: 3, hook: {insert(vn) {let nvn=w3.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});utils.defineProxy(vn4, pvnode);w3.__owl__.pvnode = pvnode;});
|
||||||
|
} else {
|
||||||
|
def2 = def2 || w3.__updateProps(props3, extra.forceUpdate, extra.patchQueue);
|
||||||
|
def2 = def2.then(()=>{if (w3.__owl__.isDestroyed) {return};let pvnode=w3.__owl__.pvnode;utils.defineProxy(vn4, pvnode);});
|
||||||
|
}
|
||||||
|
extra.promises.push(def2);
|
||||||
|
return result;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`top level sub widgets can select a sub widget 1`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
let utils = this.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let owner = context;
|
||||||
|
let result;
|
||||||
|
var h = this.utils.h;
|
||||||
|
if (context['env'].flag) {
|
||||||
|
//COMPONENT
|
||||||
|
let def2;
|
||||||
|
let w3 = 3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[3]] : false;
|
||||||
|
let vn4 = {};
|
||||||
|
result = vn4;
|
||||||
|
let props3 = {};
|
||||||
|
if (w3 && w3.__owl__.renderPromise && !w3.__owl__.vnode) {
|
||||||
|
if (utils.shallowEqual(props3, w3.__owl__.renderProps)) {
|
||||||
|
def2 = w3.__owl__.renderPromise;
|
||||||
|
} else {
|
||||||
|
w3.destroy();
|
||||||
|
w3 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!w3) {
|
||||||
|
let componentKey3 = \`Child\`;
|
||||||
|
let W3 = context.components && context.components[componentKey3] || QWeb.components[componentKey3];
|
||||||
|
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||||
|
w3 = new W3(parent, props3);
|
||||||
|
parent.__owl__.cmap[3] = w3.__owl__.id;
|
||||||
|
def2 = w3.__prepare();
|
||||||
|
def2 = def2.then(vnode=>{let pvnode=h(vnode.sel, {key: 3, hook: {insert(vn) {let nvn=w3.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});utils.defineProxy(vn4, pvnode);w3.__owl__.pvnode = pvnode;});
|
||||||
|
} else {
|
||||||
|
def2 = def2 || w3.__updateProps(props3, extra.forceUpdate, extra.patchQueue);
|
||||||
|
def2 = def2.then(()=>{if (w3.__owl__.isDestroyed) {return};let pvnode=w3.__owl__.pvnode;utils.defineProxy(vn4, pvnode);});
|
||||||
|
}
|
||||||
|
extra.promises.push(def2);
|
||||||
|
}
|
||||||
|
if (!context['env'].flag) {
|
||||||
|
//COMPONENT
|
||||||
|
let def6;
|
||||||
|
let w7 = 7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[7]] : false;
|
||||||
|
let vn8 = {};
|
||||||
|
result = vn8;
|
||||||
|
let props7 = {};
|
||||||
|
if (w7 && w7.__owl__.renderPromise && !w7.__owl__.vnode) {
|
||||||
|
if (utils.shallowEqual(props7, w7.__owl__.renderProps)) {
|
||||||
|
def6 = w7.__owl__.renderPromise;
|
||||||
|
} else {
|
||||||
|
w7.destroy();
|
||||||
|
w7 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!w7) {
|
||||||
|
let componentKey7 = \`OtherChild\`;
|
||||||
|
let W7 = context.components && context.components[componentKey7] || QWeb.components[componentKey7];
|
||||||
|
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
|
||||||
|
w7 = new W7(parent, props7);
|
||||||
|
parent.__owl__.cmap[7] = w7.__owl__.id;
|
||||||
|
def6 = w7.__prepare();
|
||||||
|
def6 = def6.then(vnode=>{let pvnode=h(vnode.sel, {key: 7, hook: {insert(vn) {let nvn=w7.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});utils.defineProxy(vn8, pvnode);w7.__owl__.pvnode = pvnode;});
|
||||||
|
} else {
|
||||||
|
def6 = def6 || w7.__updateProps(props7, extra.forceUpdate, extra.patchQueue);
|
||||||
|
def6 = def6.then(()=>{if (w7.__owl__.isDestroyed) {return};let pvnode=w7.__owl__.pvnode;utils.defineProxy(vn8, pvnode);});
|
||||||
|
}
|
||||||
|
extra.promises.push(def6);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`props validation props are validated in dev mode (code snapshot) 1`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
let utils = this.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let owner = context;
|
||||||
|
var h = this.utils.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('div', p1, c1);
|
||||||
|
//COMPONENT
|
||||||
|
let def3;
|
||||||
|
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||||
|
let _2_index = c1.length;
|
||||||
|
c1.push(null);
|
||||||
|
let props4 = {message:1};
|
||||||
|
if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode) {
|
||||||
|
if (utils.shallowEqual(props4, w4.__owl__.renderProps)) {
|
||||||
|
def3 = w4.__owl__.renderPromise;
|
||||||
|
} else {
|
||||||
|
w4.destroy();
|
||||||
|
w4 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!w4) {
|
||||||
|
let componentKey4 = \`Child\`;
|
||||||
|
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||||
|
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||||
|
utils.validateProps(W4, props4)
|
||||||
|
w4 = new W4(parent, props4);
|
||||||
|
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||||
|
def3 = w4.__prepare();
|
||||||
|
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||||
|
} else {
|
||||||
|
utils.validateProps(w4.constructor, props4)
|
||||||
|
def3 = def3 || w4.__updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||||
|
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
|
||||||
|
}
|
||||||
|
extra.promises.push(def3);
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
+584
-11
@@ -879,18 +879,22 @@ describe("composition", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("throw a nice error if it cannot find component", async () => {
|
test("display a nice error if it cannot find component", async () => {
|
||||||
expect.assertions(1);
|
const consoleError = console.error;
|
||||||
|
console.error = jest.fn();
|
||||||
|
|
||||||
env.qweb.addTemplate("Parent", `<div><SomeMispelledWidget /></div>`);
|
env.qweb.addTemplate("Parent", `<div><SomeMispelledWidget /></div>`);
|
||||||
class Parent extends Widget {
|
class Parent extends Widget {
|
||||||
components = { SomeWidget: Widget };
|
components = { SomeWidget: Widget };
|
||||||
}
|
}
|
||||||
const parent = new Parent(env);
|
const parent = new Parent(env);
|
||||||
try {
|
|
||||||
await parent.mount(fixture);
|
await parent.mount(fixture);
|
||||||
} catch (e) {
|
|
||||||
expect(e.message).toBe('Cannot find the definition of component "SomeMispelledWidget"');
|
expect(console.error).toBeCalledTimes(1);
|
||||||
}
|
expect((<any>console.error).mock.calls[0][0].message).toMatch(
|
||||||
|
'Cannot find the definition of component "SomeMispelledWidget"'
|
||||||
|
);
|
||||||
|
console.error = consoleError;
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-refs on widget are components", async () => {
|
test("t-refs on widget are components", async () => {
|
||||||
@@ -2726,7 +2730,8 @@ 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);
|
const consoleError = console.error;
|
||||||
|
console.error = jest.fn();
|
||||||
env.qweb.addTemplate("Parent", `<div><Child obj="state.obj"/></div>`);
|
env.qweb.addTemplate("Parent", `<div><Child obj="state.obj"/></div>`);
|
||||||
class Parent extends Widget {
|
class Parent extends Widget {
|
||||||
state = { obj: { coffee: 1 } };
|
state = { obj: { coffee: 1 } };
|
||||||
@@ -2739,11 +2744,13 @@ describe("widget and observable state", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const parent = new Parent(env);
|
const parent = new Parent(env);
|
||||||
try {
|
|
||||||
await parent.mount(fixture);
|
await parent.mount(fixture);
|
||||||
} catch (e) {
|
|
||||||
expect(e.message).toBe('Observed state cannot be changed here! (key: "coffee", val: "2")');
|
expect(console.error).toBeCalledTimes(1);
|
||||||
}
|
expect((<any>console.error).mock.calls[0][0].message).toMatch(
|
||||||
|
'Observed state cannot be changed here! (key: "coffee", val: "2")'
|
||||||
|
);
|
||||||
|
console.error = consoleError;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2920,6 +2927,113 @@ describe("t-slot directive", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("slots are rendered with proper context, part 2", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<a t-name="Link" t-att-href="props.to">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</a>
|
||||||
|
<div t-name="App">
|
||||||
|
<u><li t-foreach="state.users" t-as="user" t-key="user.id">
|
||||||
|
<Link to="'/user/' + user.id">User <t t-esc="user.name"/></Link>
|
||||||
|
</li></u>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Link extends Widget {}
|
||||||
|
|
||||||
|
class App extends Widget {
|
||||||
|
state = { users: [{ id: 1, name: "Aaron" }, { id: 2, name: "David" }] };
|
||||||
|
components = { Link };
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User David</a></li></u></div>'
|
||||||
|
);
|
||||||
|
expect(env.qweb.templates.Link.fn.toString()).toMatchSnapshot();
|
||||||
|
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||||
|
|
||||||
|
// test updateprops here
|
||||||
|
app.state.users[1].name = "Mathieu";
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots are rendered with proper context, part 3", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<a t-name="Link" t-att-href="props.to">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</a>
|
||||||
|
<div t-name="App">
|
||||||
|
<u><li t-foreach="state.users" t-as="user" t-key="user.id" >
|
||||||
|
<t t-set="userdescr" t-value="'User ' + user.name"/>
|
||||||
|
<Link to="'/user/' + user.id"><t t-esc="userdescr"/></Link>
|
||||||
|
</li></u>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Link extends Widget {}
|
||||||
|
|
||||||
|
class App extends Widget {
|
||||||
|
state = { users: [{ id: 1, name: "Aaron" }, { id: 2, name: "David" }] };
|
||||||
|
components = { Link };
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User David</a></li></u></div>'
|
||||||
|
);
|
||||||
|
expect(env.qweb.templates.Link.fn.toString()).toMatchSnapshot();
|
||||||
|
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||||
|
|
||||||
|
// test updateprops here
|
||||||
|
app.state.users[1].name = "Mathieu";
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots are rendered with proper context, part 4", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<a t-name="Link" t-att-href="props.to">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</a>
|
||||||
|
<div t-name="App">
|
||||||
|
<t t-set="userdescr" t-value="'User ' + state.user.name"/>
|
||||||
|
<Link to="'/user/' + state.user.id"><t t-esc="userdescr"/></Link>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Link extends Widget {}
|
||||||
|
|
||||||
|
class App extends Widget {
|
||||||
|
state = { user: { id: 1, name: "Aaron" } };
|
||||||
|
components = { Link };
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User Aaron</a></div>');
|
||||||
|
|
||||||
|
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||||
|
|
||||||
|
// test updateprops here
|
||||||
|
app.state.user.name = "David";
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User David</a></div>');
|
||||||
|
});
|
||||||
|
|
||||||
test("refs are properly bound in slots", async () => {
|
test("refs are properly bound in slots", async () => {
|
||||||
env.qweb.addTemplates(`
|
env.qweb.addTemplates(`
|
||||||
<templates>
|
<templates>
|
||||||
@@ -3087,6 +3201,76 @@ describe("t-slot directive", () => {
|
|||||||
expect(console.log).toHaveBeenCalledTimes(0);
|
expect(console.log).toHaveBeenCalledTimes(0);
|
||||||
console.log = consoleLog;
|
console.log = consoleLog;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("slot preserves properly parented relationship", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Child>
|
||||||
|
<GrandChild/>
|
||||||
|
</Child>
|
||||||
|
</div>
|
||||||
|
<div t-name="Child"><t t-slot="default"/></div>
|
||||||
|
<div t-name="GrandChild">Grand Child</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Child extends Widget {}
|
||||||
|
class GrandChild extends Widget {}
|
||||||
|
class Parent extends Widget {
|
||||||
|
components = { Child, GrandChild };
|
||||||
|
}
|
||||||
|
const parent = new Parent(env);
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div><div>Grand Child</div></div></div>");
|
||||||
|
|
||||||
|
const parentChildren = children(parent);
|
||||||
|
expect(parentChildren.length).toBe(1);
|
||||||
|
expect(parentChildren[0]).toBeInstanceOf(Child);
|
||||||
|
|
||||||
|
const childrenChildren = children(parentChildren[0]);
|
||||||
|
expect(childrenChildren.length).toBe(1);
|
||||||
|
expect(childrenChildren[0]).toBeInstanceOf(GrandChild);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slot are properly rendered if inner props are changed", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="SomeComponent">
|
||||||
|
SC:<t t-esc="props.val"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div t-name="GenericComponent">
|
||||||
|
<t t-slot="default" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="App">
|
||||||
|
<button t-on-click="inc">Inc[<t t-esc="state.val"/>]</button>
|
||||||
|
<GenericComponent>
|
||||||
|
<SomeComponent val="state.val"/>
|
||||||
|
</GenericComponent>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class SomeComponent extends Widget {}
|
||||||
|
class GenericComponent extends Widget {}
|
||||||
|
class App extends Widget {
|
||||||
|
components = { GenericComponent, SomeComponent };
|
||||||
|
state = { val: 4 };
|
||||||
|
|
||||||
|
inc() {
|
||||||
|
this.state.val++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><button>Inc[4]</button><div><div> SC:4</div></div></div>");
|
||||||
|
(<any>fixture.querySelector("button")).click();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><button>Inc[5]</button><div><div> SC:5</div></div></div>");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("t-model directive", () => {
|
describe("t-model directive", () => {
|
||||||
@@ -3347,3 +3531,392 @@ describe("environment and plugins", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div>Blue</div>");
|
expect(fixture.innerHTML).toBe("<div>Blue</div>");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("component error handling (catchError)", () => {
|
||||||
|
/**
|
||||||
|
* This test suite requires often to wait for 3 ticks. Here is why:
|
||||||
|
* - First tick is to let the app render and crash.
|
||||||
|
* - When we crash, we call the catchError handler in a setTimeout (because we
|
||||||
|
* need to wait for the previous rendering to be completely stopped). So, we
|
||||||
|
* need to wait for the second tick.
|
||||||
|
* - Then, when the handler changes the state, we need to wait for the interface
|
||||||
|
* to be rerendered.
|
||||||
|
* */
|
||||||
|
|
||||||
|
test("can catch an error in a component render function", async () => {
|
||||||
|
const consoleError = console.error;
|
||||||
|
console.error = jest.fn();
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="ErrorBoundary">
|
||||||
|
<t t-if="state.error">Error handled</t>
|
||||||
|
<t t-else="1"><t t-slot="default" /></t>
|
||||||
|
</div>
|
||||||
|
<div t-name="ErrorComponent">hey<t t-esc="props.flag and state.this.will.crash"/>
|
||||||
|
</div>
|
||||||
|
<div t-name="App">
|
||||||
|
<ErrorBoundary><ErrorComponent flag="state.flag"/></ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
</templates>`);
|
||||||
|
const handler = jest.fn();
|
||||||
|
env.qweb.on("error", null, handler);
|
||||||
|
class ErrorComponent extends Widget {}
|
||||||
|
class ErrorBoundary extends Widget {
|
||||||
|
state = { error: false };
|
||||||
|
|
||||||
|
catchError() {
|
||||||
|
this.state.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class App extends Widget {
|
||||||
|
state = { flag: false };
|
||||||
|
components = { ErrorBoundary, ErrorComponent };
|
||||||
|
}
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div><div>hey</div></div></div>");
|
||||||
|
app.state.flag = true;
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||||
|
|
||||||
|
expect(console.error).toBeCalledTimes(1);
|
||||||
|
console.error = consoleError;
|
||||||
|
expect(handler).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("no component catching error lead to full app destruction", async () => {
|
||||||
|
const handler = jest.fn();
|
||||||
|
env.qweb.on("error", null, handler);
|
||||||
|
const consoleError = console.error;
|
||||||
|
console.error = jest.fn();
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="ErrorComponent">hey<t t-esc="props.flag and state.this.will.crash"/>
|
||||||
|
</div>
|
||||||
|
<div t-name="App">
|
||||||
|
<ErrorComponent flag="state.flag"/>
|
||||||
|
</div>
|
||||||
|
</templates>`);
|
||||||
|
class ErrorComponent extends Widget {}
|
||||||
|
class App extends Widget {
|
||||||
|
state = { flag: false };
|
||||||
|
components = { ErrorComponent };
|
||||||
|
}
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>hey</div></div>");
|
||||||
|
app.state.flag = true;
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("");
|
||||||
|
|
||||||
|
expect(console.error).toBeCalledTimes(1);
|
||||||
|
console.error = consoleError;
|
||||||
|
expect(app.__owl__.isDestroyed).toBe(true);
|
||||||
|
expect(handler).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can catch an error in the initial call of a component render function", async () => {
|
||||||
|
const handler = jest.fn();
|
||||||
|
env.qweb.on("error", null, handler);
|
||||||
|
const consoleError = console.error;
|
||||||
|
console.error = jest.fn();
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="ErrorBoundary">
|
||||||
|
<t t-if="state.error">Error handled</t>
|
||||||
|
<t t-else="1"><t t-slot="default" /></t>
|
||||||
|
</div>
|
||||||
|
<div t-name="ErrorComponent">hey<t t-esc="state.this.will.crash"/>
|
||||||
|
</div>
|
||||||
|
<div t-name="App">
|
||||||
|
<ErrorBoundary><ErrorComponent /></ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
</templates>`);
|
||||||
|
class ErrorComponent extends Widget {}
|
||||||
|
class ErrorBoundary extends Widget {
|
||||||
|
state = { error: false };
|
||||||
|
|
||||||
|
catchError() {
|
||||||
|
this.state.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class App extends Widget {
|
||||||
|
components = { ErrorBoundary, ErrorComponent };
|
||||||
|
}
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||||
|
|
||||||
|
expect(console.error).toBeCalledTimes(1);
|
||||||
|
console.error = consoleError;
|
||||||
|
expect(handler).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can catch an error in the constructor call of a component render function", async () => {
|
||||||
|
const handler = jest.fn();
|
||||||
|
env.qweb.on("error", null, handler);
|
||||||
|
const consoleError = console.error;
|
||||||
|
console.error = jest.fn();
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="ErrorBoundary">
|
||||||
|
<t t-if="state.error">Error handled</t>
|
||||||
|
<t t-else="1"><t t-slot="default" /></t>
|
||||||
|
</div>
|
||||||
|
<div t-name="ErrorComponent">Some text</div>
|
||||||
|
<div t-name="App">
|
||||||
|
<ErrorBoundary><ErrorComponent /></ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
</templates>`);
|
||||||
|
class ErrorComponent extends Widget {
|
||||||
|
constructor(parent) {
|
||||||
|
super(parent);
|
||||||
|
throw new Error("NOOOOO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ErrorBoundary extends Widget {
|
||||||
|
state = { error: false };
|
||||||
|
|
||||||
|
catchError() {
|
||||||
|
this.state.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class App extends Widget {
|
||||||
|
components = { ErrorBoundary, ErrorComponent };
|
||||||
|
}
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||||
|
|
||||||
|
expect(console.error).toBeCalledTimes(1);
|
||||||
|
console.error = consoleError;
|
||||||
|
expect(handler).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can catch an error in the willStart call", async () => {
|
||||||
|
const consoleError = console.error;
|
||||||
|
console.error = jest.fn();
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="ErrorBoundary">
|
||||||
|
<t t-if="state.error">Error handled</t>
|
||||||
|
<t t-else="1"><t t-slot="default" /></t>
|
||||||
|
</div>
|
||||||
|
<div t-name="ErrorComponent">Some text</div>
|
||||||
|
<div t-name="App">
|
||||||
|
<ErrorBoundary><ErrorComponent /></ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
</templates>`);
|
||||||
|
class ErrorComponent extends Widget {
|
||||||
|
async willStart() {
|
||||||
|
// we wait a little bit to be in a different stack frame
|
||||||
|
await nextTick();
|
||||||
|
throw new Error("NOOOOO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ErrorBoundary extends Widget {
|
||||||
|
state = { error: false };
|
||||||
|
|
||||||
|
catchError() {
|
||||||
|
this.state.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class App extends Widget {
|
||||||
|
components = { ErrorBoundary, ErrorComponent };
|
||||||
|
}
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||||
|
|
||||||
|
expect(console.error).toBeCalledTimes(1);
|
||||||
|
console.error = consoleError;
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can catch an error in the mounted call", async () => {
|
||||||
|
console.error = jest.fn();
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="ErrorBoundary">
|
||||||
|
<t t-if="state.error">Error handled</t>
|
||||||
|
<t t-else="1"><t t-slot="default" /></t>
|
||||||
|
</div>
|
||||||
|
<div t-name="ErrorComponent">Some text</div>
|
||||||
|
<div t-name="App">
|
||||||
|
<ErrorBoundary><ErrorComponent /></ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
</templates>`);
|
||||||
|
class ErrorComponent extends Widget {
|
||||||
|
mounted() {
|
||||||
|
throw new Error("NOOOOO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ErrorBoundary extends Widget {
|
||||||
|
state = { error: false };
|
||||||
|
|
||||||
|
catchError() {
|
||||||
|
this.state.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class App extends Widget {
|
||||||
|
components = { ErrorBoundary, ErrorComponent };
|
||||||
|
}
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can catch an error in the willPatch call", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="ErrorBoundary">
|
||||||
|
<t t-if="state.error">Error handled</t>
|
||||||
|
<t t-else="1"><t t-slot="default" /></t>
|
||||||
|
</div>
|
||||||
|
<div t-name="ErrorComponent"><t t-esc="props.message"/></div>
|
||||||
|
<div t-name="App">
|
||||||
|
<ErrorBoundary><ErrorComponent message="state.message" /></ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
</templates>`);
|
||||||
|
class ErrorComponent extends Widget {
|
||||||
|
willPatch() {
|
||||||
|
throw new Error("NOOOOO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ErrorBoundary extends Widget {
|
||||||
|
state = { error: false };
|
||||||
|
|
||||||
|
catchError() {
|
||||||
|
this.state.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class App extends Widget {
|
||||||
|
state = { message: "abc" };
|
||||||
|
components = { ErrorBoundary, ErrorComponent };
|
||||||
|
}
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div><div>abc</div></div></div>");
|
||||||
|
app.state.message = "def";
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("top level sub widgets", () => {
|
||||||
|
test("basic use", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<t t-name="Parent">
|
||||||
|
<Child p="1"/>
|
||||||
|
</t>
|
||||||
|
<span t-name="Child">child<t t-esc="props.p"/></span>
|
||||||
|
</templates>`);
|
||||||
|
class Child extends Widget {}
|
||||||
|
class Parent extends Widget {
|
||||||
|
components = { Child };
|
||||||
|
}
|
||||||
|
const parent = new Parent(env);
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<span>child1</span>");
|
||||||
|
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test("sub widget is interactive", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<t t-name="Parent">
|
||||||
|
<Child p="1"/>
|
||||||
|
</t>
|
||||||
|
<span t-name="Child"><button t-on-click="inc">click</button>child<t t-esc="state.val"/></span>
|
||||||
|
</templates>`);
|
||||||
|
class Child extends Widget {
|
||||||
|
state = {val: 1};
|
||||||
|
inc() {
|
||||||
|
this.state.val++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Parent extends Widget {
|
||||||
|
components = { Child };
|
||||||
|
}
|
||||||
|
const parent = new Parent(env);
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<span><button>click</button>child1</span>");
|
||||||
|
const button = fixture.querySelector('button')!;
|
||||||
|
button.click();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<span><button>click</button>child2</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can select a sub widget ", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<t t-name="Parent">
|
||||||
|
<t t-if="env.flag"><Child /></t>
|
||||||
|
<t t-if="!env.flag"><OtherChild /></t>
|
||||||
|
</t>
|
||||||
|
<span t-name="Child">CHILD 1</span>
|
||||||
|
<div t-name="OtherChild">CHILD 2</div>
|
||||||
|
</templates>`);
|
||||||
|
class Child extends Widget {}
|
||||||
|
class OtherChild extends Widget {}
|
||||||
|
class Parent extends Widget {
|
||||||
|
components = { Child, OtherChild };
|
||||||
|
}
|
||||||
|
(<any>env).flag = true;
|
||||||
|
let parent = new Parent(env);
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<span>CHILD 1</span>");
|
||||||
|
parent.destroy();
|
||||||
|
(<any>env).flag = false;
|
||||||
|
parent = new Parent(env);
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div>CHILD 2</div>");
|
||||||
|
|
||||||
|
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can select a sub widget, part 2", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<t t-name="Parent">
|
||||||
|
<t t-if="state.flag"><Child /></t>
|
||||||
|
<t t-if="!state.flag"><OtherChild /></t>
|
||||||
|
</t>
|
||||||
|
<span t-name="Child">CHILD 1</span>
|
||||||
|
<div t-name="OtherChild">CHILD 2</div>
|
||||||
|
</templates>`);
|
||||||
|
class Child extends Widget {}
|
||||||
|
class OtherChild extends Widget {}
|
||||||
|
class Parent extends Widget {
|
||||||
|
state = {flag: true}
|
||||||
|
components = { Child, OtherChild };
|
||||||
|
}
|
||||||
|
let parent = new Parent(env);
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<span>CHILD 1</span>");
|
||||||
|
parent.state.flag = false;
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div>CHILD 2</div>");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|||||||
+14
-1
@@ -68,9 +68,22 @@ export function renderToDOM(
|
|||||||
return result.elm as HTMLElement;
|
return result.elm as HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a template to an html string. The big difference with the
|
||||||
|
* renderToString method in QWeb is that we use the renderToDom method, which
|
||||||
|
* snapshots the resulting template function. Doing so gives us a large body
|
||||||
|
* of reference to evaluate changes in the generated QWeb code.
|
||||||
|
*
|
||||||
|
* Note that the result of renderToString is guaranteed to be the same as the
|
||||||
|
* one from QWeb.
|
||||||
|
*/
|
||||||
export function renderToString(qweb: QWeb, t: string, context: EvalContext = {}): string {
|
export function renderToString(qweb: QWeb, t: string, context: EvalContext = {}): string {
|
||||||
const node = renderToDOM(qweb, t, context);
|
const node = renderToDOM(qweb, t, context);
|
||||||
return node instanceof Text ? node.textContent! : node.outerHTML;
|
const result = node instanceof Text ? node.textContent! : node.outerHTML;
|
||||||
|
if (result !== qweb.renderToString(t, context)) {
|
||||||
|
throw new Error("HTML string returned by renderToString helper does not match QWeb render");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// hereafter, we define two helpers to patch/unpatch the nextFrame utils. This
|
// hereafter, we define two helpers to patch/unpatch the nextFrame utils. This
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, Env } from "../src/component";
|
import { Component, Env } from "../src/component";
|
||||||
import { makeTestFixture, makeTestEnv } from "./helpers";
|
import { makeTestFixture, makeTestEnv } from "./helpers";
|
||||||
import { QWeb } from "../src";
|
import { QWeb, UTILS } from "../src/qweb_core";
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Setup and helpers
|
// Setup and helpers
|
||||||
@@ -45,19 +45,6 @@ describe("props validation", () => {
|
|||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("props validation is also done on update props", async () => {
|
|
||||||
expect.assertions(1);
|
|
||||||
class TestWidget extends Widget {
|
|
||||||
static props = ["message"];
|
|
||||||
}
|
|
||||||
const w = new TestWidget(env, { message: "bottle" });
|
|
||||||
try {
|
|
||||||
await w.__updateProps({});
|
|
||||||
} catch (e) {
|
|
||||||
expect(e.message).toBe("Missing props 'message' (component 'TestWidget')");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("props: list of strings", async () => {
|
test("props: list of strings", async () => {
|
||||||
class TestWidget extends Widget {
|
class TestWidget extends Widget {
|
||||||
static props = ["message"];
|
static props = ["message"];
|
||||||
@@ -234,6 +221,96 @@ describe("props validation", () => {
|
|||||||
new TestWidget(env, { p: { id: 1, url: [12, true] } });
|
new TestWidget(env, { p: { id: 1, url: [12, true] } });
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("props are validated in dev mode (code snapshot)", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="App">
|
||||||
|
<Child message="1"/>
|
||||||
|
</div>
|
||||||
|
<div t-name="Child"><t t-esc="props.message"/></div>
|
||||||
|
</templates>`);
|
||||||
|
|
||||||
|
class Child extends Widget {
|
||||||
|
static props = ["message"];
|
||||||
|
}
|
||||||
|
class App extends Widget {
|
||||||
|
components = { Child };
|
||||||
|
}
|
||||||
|
const app = new App(env);
|
||||||
|
await app.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>1</div></div>");
|
||||||
|
// need to make sure there are 2 call to update props. one at component
|
||||||
|
// creation, and one at update time.
|
||||||
|
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("props: list of strings with optional props", async () => {
|
||||||
|
class TestWidget extends Widget {
|
||||||
|
static props = ["message", "someProp?"];
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
UTILS.validateProps(TestWidget, { someProp: 1 });
|
||||||
|
}).toThrow();
|
||||||
|
expect(() => {
|
||||||
|
UTILS.validateProps(TestWidget, { message: 1 });
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("props: can be defined with a boolean", async () => {
|
||||||
|
class TestWidget extends Widget {
|
||||||
|
static props = { message: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
UTILS.validateProps(TestWidget, {});
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("props: extra props cause an error", async () => {
|
||||||
|
class TestWidget extends Widget {
|
||||||
|
static props = ["message"];
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
UTILS.validateProps(TestWidget, { message: 1, flag: true });
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("props: extra props cause an error, part 2", async () => {
|
||||||
|
class TestWidget extends Widget {
|
||||||
|
static props = {message: true};
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
UTILS.validateProps(TestWidget, { message: 1, flag: true });
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("props: optional prop do not cause an error", async () => {
|
||||||
|
class TestWidget extends Widget {
|
||||||
|
static props = ["message?"];
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
UTILS.validateProps(TestWidget, { message: 1});
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("optional prop do not cause an error if value is undefined", async () => {
|
||||||
|
class TestWidget extends Widget {
|
||||||
|
static props = {message: {type: String, optional: true}};
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
UTILS.validateProps(TestWidget, { message: undefined});
|
||||||
|
}).not.toThrow();
|
||||||
|
expect(() => {
|
||||||
|
UTILS.validateProps(TestWidget, { message: null});
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("default props", () => {
|
describe("default props", () => {
|
||||||
|
|||||||
+192
-279
@@ -1,5 +1,5 @@
|
|||||||
import { Component, Env } from "../src/component";
|
import { Component, Env } from "../src/component";
|
||||||
import { connect, Store } from "../src/store";
|
import { Store, ConnectedComponent } from "../src/store";
|
||||||
import { makeTestFixture, makeTestEnv, nextMicroTick, nextTick } from "./helpers";
|
import { makeTestFixture, makeTestEnv, nextMicroTick, nextTick } from "./helpers";
|
||||||
import { Observer } from "../src";
|
import { Observer } from "../src";
|
||||||
|
|
||||||
@@ -561,18 +561,21 @@ describe("connecting a component to store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("connecting a component works", async () => {
|
test("connecting a component works", async () => {
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplates(`
|
||||||
"App",
|
<templates>
|
||||||
`
|
<div t-name="App">
|
||||||
<div>
|
|
||||||
<t t-foreach="props.todos" t-as="todo" >
|
<t t-foreach="props.todos" t-as="todo" >
|
||||||
<Todo msg="todo.msg" t-key="todo"/>
|
<Todo msg="todo.msg" t-key="todo"/>
|
||||||
</t>
|
</t>
|
||||||
</div>`
|
</div>
|
||||||
);
|
<span t-name="Todo"><t t-esc="props.msg"/></span>
|
||||||
env.qweb.addTemplate("Todo", `<span><t t-esc="props.msg"/></span>`);
|
</templates>
|
||||||
class App extends Component<any, any, any> {
|
`);
|
||||||
|
class App extends ConnectedComponent<any, any, any> {
|
||||||
components = { Todo };
|
components = { Todo };
|
||||||
|
static mapStoreToProps(s) {
|
||||||
|
return { todos: s.todos };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class Todo extends Component<any, any, any> {}
|
class Todo extends Component<any, any, any> {}
|
||||||
const state = { todos: [] };
|
const state = { todos: [] };
|
||||||
@@ -581,16 +584,9 @@ describe("connecting a component to store", () => {
|
|||||||
state.todos.push({ msg });
|
state.todos.push({ msg });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
function mapStoreToProps(s) {
|
|
||||||
return { todos: s.todos };
|
|
||||||
}
|
|
||||||
const TodoApp = connect(
|
|
||||||
App,
|
|
||||||
mapStoreToProps
|
|
||||||
);
|
|
||||||
const store = new Store({ state, mutations });
|
const store = new Store({ state, mutations });
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new TodoApp(env);
|
const app = new App(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toMatchSnapshot();
|
expect(fixture.innerHTML).toMatchSnapshot();
|
||||||
@@ -601,38 +597,35 @@ describe("connecting a component to store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("deep and shallow connecting a component", async () => {
|
test("deep and shallow connecting a component", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="App">
|
||||||
|
<span t-foreach="props.todos" t-as="todo" t-key="todo">
|
||||||
|
<t t-esc="todo.title"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
const state = { todos: [{ title: "Kasteel" }] };
|
const state = { todos: [{ title: "Kasteel" }] };
|
||||||
const mutations = {
|
const mutations = {
|
||||||
edit({ state }, title) {
|
edit({ state }, title) {
|
||||||
state.todos[0].title = title;
|
state.todos[0].title = title;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
function mapStoreToProps(s) {
|
|
||||||
return { todos: s.todos };
|
|
||||||
}
|
|
||||||
const store = new Store({ state, mutations });
|
const store = new Store({ state, mutations });
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
class App extends ConnectedComponent<any, any, any> {
|
||||||
"App",
|
static mapStoreToProps(s) {
|
||||||
`
|
return { todos: s.todos };
|
||||||
<div>
|
}
|
||||||
<span t-foreach="props.todos" t-as="todo" t-key="todo">
|
}
|
||||||
<t t-esc="todo.title"/>
|
class DeepTodoApp extends App {
|
||||||
</span>
|
deep = true;
|
||||||
</div>`
|
}
|
||||||
);
|
class ShallowTodoApp extends App {
|
||||||
class App extends Component<any, any, any> {}
|
deep = false;
|
||||||
|
}
|
||||||
|
|
||||||
const DeepTodoApp = connect(
|
|
||||||
App,
|
|
||||||
mapStoreToProps,
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
const ShallowTodoApp = connect(
|
|
||||||
App,
|
|
||||||
mapStoreToProps,
|
|
||||||
{ deep: false }
|
|
||||||
);
|
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const deepTodoApp = new DeepTodoApp(env);
|
const deepTodoApp = new DeepTodoApp(env);
|
||||||
const shallowTodoApp = new ShallowTodoApp(env);
|
const shallowTodoApp = new ShallowTodoApp(env);
|
||||||
@@ -662,9 +655,6 @@ describe("connecting a component to store", () => {
|
|||||||
<span t-name="Todo"><t t-esc="props.msg"/></span>
|
<span t-name="Todo"><t t-esc="props.msg"/></span>
|
||||||
</templates>
|
</templates>
|
||||||
`);
|
`);
|
||||||
class App extends Component<any, any, any> {
|
|
||||||
components = { Todo };
|
|
||||||
}
|
|
||||||
class Todo extends Component<any, any, any> {}
|
class Todo extends Component<any, any, any> {}
|
||||||
|
|
||||||
(<any>env).store = new Store({});
|
(<any>env).store = new Store({});
|
||||||
@@ -676,17 +666,16 @@ describe("connecting a component to store", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
function mapStoreToProps(s) {
|
class App extends ConnectedComponent<any, any, any> {
|
||||||
|
components = { Todo };
|
||||||
|
static mapStoreToProps(s) {
|
||||||
return { todos: s.todos };
|
return { todos: s.todos };
|
||||||
}
|
}
|
||||||
const TodoApp = connect(
|
getStore() {
|
||||||
App,
|
return store;
|
||||||
mapStoreToProps,
|
|
||||||
{
|
|
||||||
getStore: () => store
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
const app = new TodoApp(env);
|
const app = new App(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toMatchSnapshot();
|
expect(fixture.innerHTML).toMatchSnapshot();
|
||||||
@@ -698,8 +687,18 @@ describe("connecting a component to store", () => {
|
|||||||
|
|
||||||
test("connected child components with custom hooks", async () => {
|
test("connected child components with custom hooks", async () => {
|
||||||
let steps: any = [];
|
let steps: any = [];
|
||||||
env.qweb.addTemplate("Child", `<div/>`);
|
env.qweb.addTemplates(`
|
||||||
class Child extends Component<any, any, any> {
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Child t-if="state.child" />
|
||||||
|
</div>
|
||||||
|
<div t-name="Child"/>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Child extends ConnectedComponent<any, any, any> {
|
||||||
|
static mapStoreToProps(s) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
mounted() {
|
mounted() {
|
||||||
steps.push("child:mounted");
|
steps.push("child:mounted");
|
||||||
}
|
}
|
||||||
@@ -708,20 +707,8 @@ describe("connecting a component to store", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectedChild = connect(
|
|
||||||
Child,
|
|
||||||
s => s
|
|
||||||
);
|
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
|
||||||
"Parent",
|
|
||||||
`
|
|
||||||
<div>
|
|
||||||
<t t-if="state.child" t-component="ConnectedChild"/>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
class Parent extends Component<any, any, any> {
|
class Parent extends Component<any, any, any> {
|
||||||
components = { ConnectedChild };
|
components = { Child };
|
||||||
|
|
||||||
constructor(env: Env) {
|
constructor(env: Env) {
|
||||||
super(env);
|
super(env);
|
||||||
@@ -741,7 +728,7 @@ describe("connecting a component to store", () => {
|
|||||||
expect(steps).toEqual(["child:mounted", "child:willUnmount"]);
|
expect(steps).toEqual(["child:mounted", "child:willUnmount"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("connect receives ownprops as second argument", async () => {
|
test("mapStoreToProps receives ownprops as second argument", async () => {
|
||||||
const state = { todos: [{ id: 1, text: "jupiler" }] };
|
const state = { todos: [{ id: 1, text: "jupiler" }] };
|
||||||
let nextId = 2;
|
let nextId = 2;
|
||||||
const mutations = {
|
const mutations = {
|
||||||
@@ -751,38 +738,32 @@ describe("connecting a component to store", () => {
|
|||||||
};
|
};
|
||||||
const store = new Store({ state, mutations });
|
const store = new Store({ state, mutations });
|
||||||
|
|
||||||
env.qweb.addTemplate("TodoItem", `<span><t t-esc="props.text"/></span>`);
|
env.qweb.addTemplates(`
|
||||||
class TodoItem extends Component<any, any, any> {}
|
<templates>
|
||||||
const ConnectedTodo = connect(
|
<span t-name="TodoItem"><t t-esc="props.text"/></span>
|
||||||
TodoItem,
|
<div t-name="TodoList">
|
||||||
(state, props) => {
|
<t t-foreach="props.todos" t-as="todo">
|
||||||
|
<TodoItem id="todo.id" t-key="todo.id"/>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class TodoItem extends ConnectedComponent<any, any, any> {
|
||||||
|
static mapStoreToProps(state, props) {
|
||||||
const todo = state.todos.find(t => t.id === props.id);
|
const todo = state.todos.find(t => t.id === props.id);
|
||||||
return todo;
|
return todo;
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
|
||||||
"TodoList",
|
|
||||||
`<div>
|
|
||||||
<t t-foreach="props.todos" t-as="todo">
|
|
||||||
<ConnectedTodo id="todo.id" t-key="todo.id"/>
|
|
||||||
</t>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
class TodoList extends Component<any, any, any> {
|
|
||||||
components = { ConnectedTodo };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStoreToProps(state) {
|
class TodoList extends ConnectedComponent<any, any, any> {
|
||||||
|
components = { TodoItem };
|
||||||
|
static mapStoreToProps(state) {
|
||||||
return { todos: state.todos };
|
return { todos: state.todos };
|
||||||
}
|
}
|
||||||
const ConnectedTodoList = connect(
|
}
|
||||||
TodoList,
|
|
||||||
mapStoreToProps
|
|
||||||
);
|
|
||||||
|
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new ConnectedTodoList(env);
|
const app = new TodoList(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><span>jupiler</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>jupiler</span></div>");
|
||||||
@@ -792,7 +773,7 @@ describe("connecting a component to store", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><span>jupiler</span><span>hoegaarden</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>jupiler</span><span>hoegaarden</span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("connect receives store getters as third argument", async () => {
|
test("mapStoreToProps receives store getters as third argument", async () => {
|
||||||
const state = {
|
const state = {
|
||||||
importantID: 1,
|
importantID: 1,
|
||||||
todos: [{ id: 1, text: "jupiler" }, { id: 2, text: "bertinchamps" }]
|
todos: [{ id: 1, text: "jupiler" }, { id: 2, text: "bertinchamps" }]
|
||||||
@@ -807,47 +788,39 @@ describe("connecting a component to store", () => {
|
|||||||
};
|
};
|
||||||
const store = new Store({ state, getters });
|
const store = new Store({ state, getters });
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplates(`
|
||||||
"TodoItem",
|
<templates>
|
||||||
`<div>
|
<div t-name="TodoItem">
|
||||||
<span><t t-esc="props.activeTodoText"/></span>
|
<span><t t-esc="props.activeTodoText"/></span>
|
||||||
<span><t t-esc="props.importantTodoText"/></span>
|
<span><t t-esc="props.importantTodoText"/></span>
|
||||||
</div>`
|
</div>
|
||||||
);
|
<div t-name="TodoList">
|
||||||
class TodoItem extends Component<any, any, any> {}
|
<t t-foreach="props.todos" t-as="todo">
|
||||||
const ConnectedTodo = connect(
|
<TodoItem id="todo.id" t-key="todo.id"/>
|
||||||
TodoItem,
|
</t>
|
||||||
(state, props, getters) => {
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
|
||||||
|
class TodoItem extends ConnectedComponent<any, any, any> {
|
||||||
|
static mapStoreToProps(state, props, getters) {
|
||||||
const todo = state.todos.find(t => t.id === props.id);
|
const todo = state.todos.find(t => t.id === props.id);
|
||||||
return {
|
return {
|
||||||
activeTodoText: getters.text(todo.id),
|
activeTodoText: getters.text(todo.id),
|
||||||
importantTodoText: getters.importantTodoText()
|
importantTodoText: getters.importantTodoText()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
|
||||||
"TodoList",
|
|
||||||
`<div>
|
|
||||||
<t t-foreach="props.todos" t-as="todo">
|
|
||||||
<ConnectedTodo id="todo.id" t-key="todo.id"/>
|
|
||||||
</t>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
class TodoList extends Component<any, any, any> {
|
|
||||||
components = { ConnectedTodo };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStoreToProps(state) {
|
class TodoList extends ConnectedComponent<any, any, any> {
|
||||||
|
components = { TodoItem };
|
||||||
|
static mapStoreToProps(state) {
|
||||||
return { todos: state.todos };
|
return { todos: state.todos };
|
||||||
}
|
}
|
||||||
const ConnectedTodoList = connect(
|
}
|
||||||
TodoList,
|
|
||||||
mapStoreToProps
|
|
||||||
);
|
|
||||||
|
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new ConnectedTodoList(env);
|
const app = new TodoList(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe(
|
expect(fixture.innerHTML).toBe(
|
||||||
@@ -856,23 +829,23 @@ describe("connecting a component to store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("connected component is updated when props are updated", async () => {
|
test("connected component is updated when props are updated", async () => {
|
||||||
env.qweb.addTemplate("Beer", `<span><t t-esc="props.name"/></span>`);
|
env.qweb.addTemplates(`
|
||||||
class Beer extends Component<any, any, any> {}
|
<templates>
|
||||||
const ConnectedBeer = connect(
|
<span t-name="Beer"><t t-esc="props.name"/></span>
|
||||||
Beer,
|
<div t-name="App">
|
||||||
(state, props) => {
|
<Beer id="state.beerId"/>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
|
||||||
|
class Beer extends ConnectedComponent<any, any, any> {
|
||||||
|
static mapStoreToProps(state, props) {
|
||||||
return state.beers[props.id];
|
return state.beers[props.id];
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
|
||||||
"App",
|
|
||||||
`<div>
|
|
||||||
<ConnectedBeer id="state.beerId"/>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
class App extends Component<any, any, any> {
|
class App extends Component<any, any, any> {
|
||||||
components = { ConnectedBeer };
|
components = { Beer };
|
||||||
state = { beerId: 1 };
|
state = { beerId: 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -890,14 +863,19 @@ describe("connecting a component to store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("connected component is updated when store is changed", async () => {
|
test("connected component is updated when store is changed", async () => {
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplates(`
|
||||||
"App",
|
<templates>
|
||||||
`
|
<div t-name="App">
|
||||||
<div>
|
|
||||||
<span t-foreach="props.beers" t-as="beer" t-key="beer.name"><t t-esc="beer.name"/></span>
|
<span t-foreach="props.beers" t-as="beer" t-key="beer.name"><t t-esc="beer.name"/></span>
|
||||||
</div>`
|
</div>
|
||||||
);
|
</templates>
|
||||||
class App extends Component<any, any, any> {}
|
`);
|
||||||
|
|
||||||
|
class App extends ConnectedComponent<any, any, any> {
|
||||||
|
static mapStoreToProps(state) {
|
||||||
|
return { beers: state.beers, otherKey: 1 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
addBeer({ state }, name) {
|
addBeer({ state }, name) {
|
||||||
@@ -909,14 +887,7 @@ describe("connecting a component to store", () => {
|
|||||||
const store = new Store({ state, mutations });
|
const store = new Store({ state, mutations });
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
|
|
||||||
function mapStoreToProps(state) {
|
const app = new App(env);
|
||||||
return { beers: state.beers, otherKey: 1 };
|
|
||||||
}
|
|
||||||
const ConnectedApp = connect(
|
|
||||||
App,
|
|
||||||
mapStoreToProps
|
|
||||||
);
|
|
||||||
const app = new ConnectedApp(env);
|
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><span>jupiler</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>jupiler</span></div>");
|
||||||
@@ -927,34 +898,31 @@ describe("connecting a component to store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("connected component with undefined, null and string props", async () => {
|
test("connected component with undefined, null and string props", async () => {
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplates(`
|
||||||
"Beer",
|
<templates>
|
||||||
`<div>
|
<div t-name="Beer">
|
||||||
<span>taster:<t t-esc="props.taster"/></span>
|
<span>taster:<t t-esc="props.taster"/></span>
|
||||||
<span t-if="props.selected">selected:<t t-esc="props.selected.name"/></span>
|
<span t-if="props.selected">selected:<t t-esc="props.selected.name"/></span>
|
||||||
<span t-if="props.consumed">consumed:<t t-esc="props.consumed.name"/></span>
|
<span t-if="props.consumed">consumed:<t t-esc="props.consumed.name"/></span>
|
||||||
</div>`
|
</div>
|
||||||
);
|
<div t-name="App">
|
||||||
class Beer extends Component<any, any, any> {}
|
<Beer id="state.beerId"/>
|
||||||
const ConnectedBeer = connect(
|
</div>
|
||||||
Beer,
|
</templates>
|
||||||
(state, props) => {
|
`);
|
||||||
|
|
||||||
|
class Beer extends ConnectedComponent<any, any, any> {
|
||||||
|
static mapStoreToProps(state, props) {
|
||||||
return {
|
return {
|
||||||
selected: state.beers[props.id],
|
selected: state.beers[props.id],
|
||||||
consumed: state.beers[state.consumedID] || null,
|
consumed: state.beers[state.consumedID] || null,
|
||||||
taster: state.taster
|
taster: state.taster
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
|
||||||
"App",
|
|
||||||
`<div>
|
|
||||||
<ConnectedBeer id="state.beerId"/>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
class App extends Component<any, any, any> {
|
class App extends Component<any, any, any> {
|
||||||
components = { ConnectedBeer };
|
components = { Beer };
|
||||||
state = { beerId: 0 };
|
state = { beerId: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -997,34 +965,31 @@ describe("connecting a component to store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("connected component deeply reactive with undefined, null and string props", async () => {
|
test("connected component deeply reactive with undefined, null and string props", async () => {
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplates(`
|
||||||
"Beer",
|
<templates>
|
||||||
`<div>
|
<div t-name="Beer">
|
||||||
<span>taster:<t t-esc="props.taster"/></span>
|
<span>taster:<t t-esc="props.taster"/></span>
|
||||||
<span t-if="props.selected">selected:<t t-esc="props.selected.name"/></span>
|
<span t-if="props.selected">selected:<t t-esc="props.selected.name"/></span>
|
||||||
<span t-if="props.consumed">consumed:<t t-esc="props.consumed.name"/></span>
|
<span t-if="props.consumed">consumed:<t t-esc="props.consumed.name"/></span>
|
||||||
</div>`
|
</div>
|
||||||
);
|
<div t-name="App">
|
||||||
class Beer extends Component<any, any, any> {}
|
<Beer id="state.beerId"/>
|
||||||
const ConnectedBeer = connect(
|
</div>
|
||||||
Beer,
|
</templates>
|
||||||
(state, props) => {
|
`);
|
||||||
|
|
||||||
|
class Beer extends ConnectedComponent<any, any, any> {
|
||||||
|
static mapStoreToProps(storeState, props) {
|
||||||
return {
|
return {
|
||||||
selected: state.beers[props.id],
|
selected: storeState.beers[props.id],
|
||||||
consumed: state.beers[state.consumedID] || null,
|
consumed: storeState.beers[storeState.consumedID] || null,
|
||||||
taster: state.taster
|
taster: storeState.taster
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
|
||||||
"App",
|
|
||||||
`<div>
|
|
||||||
<ConnectedBeer id="state.beerId"/>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
class App extends Component<any, any, any> {
|
class App extends Component<any, any, any> {
|
||||||
components = { ConnectedBeer };
|
components = { Beer };
|
||||||
state = { beerId: 0 };
|
state = { beerId: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1093,35 +1058,29 @@ describe("connecting a component to store", () => {
|
|||||||
test("correct update order when parent/children are connected", async () => {
|
test("correct update order when parent/children are connected", async () => {
|
||||||
const steps: string[] = [];
|
const steps: string[] = [];
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplates(`
|
||||||
"Parent",
|
<templates>
|
||||||
`
|
<div t-name="Parent">
|
||||||
<div>
|
|
||||||
<Child key="props.current"/>
|
<Child key="props.current"/>
|
||||||
</div>
|
</div>
|
||||||
`
|
<span t-name="Child"><t t-esc="props.msg"/></span>
|
||||||
);
|
</templates>
|
||||||
class Parent extends Component<any, any, any> {
|
`);
|
||||||
components = { Child: ConnectedChild };
|
|
||||||
}
|
class Parent extends ConnectedComponent<any, any, any> {
|
||||||
const ConnectedParent = connect(
|
components = { Child };
|
||||||
Parent,
|
static mapStoreToProps(s) {
|
||||||
function(s) {
|
|
||||||
steps.push("parent");
|
steps.push("parent");
|
||||||
return { current: s.current, isvisible: s.isvisible };
|
return { current: s.current, isvisible: s.isvisible };
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
env.qweb.addTemplate("Child", `<span><t t-esc="props.msg"/></span>`);
|
class Child extends ConnectedComponent<any, any, any> {
|
||||||
class Child extends Component<any, any, any> {}
|
static mapStoreToProps(s, props) {
|
||||||
|
|
||||||
const ConnectedChild = connect(
|
|
||||||
Child,
|
|
||||||
function(s, props) {
|
|
||||||
steps.push("child");
|
steps.push("child");
|
||||||
return { msg: s.msg[props.key] };
|
return { msg: s.msg[props.key] };
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
const state = { current: "a", msg: { a: "a", b: "b" } };
|
const state = { current: "a", msg: { a: "a", b: "b" } };
|
||||||
const mutations = {
|
const mutations = {
|
||||||
@@ -1132,7 +1091,7 @@ describe("connecting a component to store", () => {
|
|||||||
|
|
||||||
const store = new Store({ state, mutations });
|
const store = new Store({ state, mutations });
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new ConnectedParent(env);
|
const app = new Parent(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><span>a</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>a</span></div>");
|
||||||
@@ -1163,7 +1122,7 @@ describe("connecting a component to store", () => {
|
|||||||
<templates>
|
<templates>
|
||||||
<div t-name="TodoApp" class="todoapp">
|
<div t-name="TodoApp" class="todoapp">
|
||||||
<t t-foreach="Object.values(props.todos)" t-as="todo">
|
<t t-foreach="Object.values(props.todos)" t-as="todo">
|
||||||
<ConnectedTodoItem t-key="todo.id" id="todo.id"/>
|
<TodoItem t-key="todo.id" id="todo.id"/>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1174,34 +1133,27 @@ describe("connecting a component to store", () => {
|
|||||||
</templates>
|
</templates>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
function mapStoreToPropsTodoApp(state) {
|
class TodoApp extends ConnectedComponent<any, any, any> {
|
||||||
|
components = { TodoItem };
|
||||||
|
static mapStoreToProps(state) {
|
||||||
return {
|
return {
|
||||||
todos: state.todos
|
todos: state.todos
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class TodoApp extends Component<any, any, any> {
|
|
||||||
components = { ConnectedTodoItem };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectedTodoApp = connect(
|
|
||||||
TodoApp,
|
|
||||||
mapStoreToPropsTodoApp
|
|
||||||
);
|
|
||||||
|
|
||||||
let renderCount = 0;
|
let renderCount = 0;
|
||||||
let fCount = 0;
|
let fCount = 0;
|
||||||
|
|
||||||
function mapStoreToPropsTodoItem(state, ownProps) {
|
class TodoItem extends ConnectedComponent<any, any, any> {
|
||||||
|
state = { isEditing: false };
|
||||||
|
static mapStoreToProps(state, ownProps) {
|
||||||
fCount++;
|
fCount++;
|
||||||
return {
|
return {
|
||||||
todo: state.todos[ownProps.id]
|
todo: state.todos[ownProps.id]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class TodoItem extends Component<any, any, any> {
|
|
||||||
state = { isEditing: false };
|
|
||||||
|
|
||||||
editTodo() {
|
editTodo() {
|
||||||
this.env.store.commit("editTodo");
|
this.env.store.commit("editTodo");
|
||||||
}
|
}
|
||||||
@@ -1211,13 +1163,8 @@ describe("connecting a component to store", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectedTodoItem = connect(
|
|
||||||
TodoItem,
|
|
||||||
mapStoreToPropsTodoItem
|
|
||||||
);
|
|
||||||
|
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new ConnectedTodoApp(env);
|
const app = new TodoApp(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe(
|
expect(fixture.innerHTML).toBe(
|
||||||
@@ -1254,7 +1201,7 @@ describe("connecting a component to store", () => {
|
|||||||
<templates>
|
<templates>
|
||||||
<div t-name="TodoApp" class="todoapp">
|
<div t-name="TodoApp" class="todoapp">
|
||||||
<t t-foreach="Object.values(props.todos)" t-as="todo">
|
<t t-foreach="Object.values(props.todos)" t-as="todo">
|
||||||
<ConnectedTodoItem t-key="todo.id" id="todo.id"/>
|
<TodoItem t-key="todo.id" id="todo.id"/>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1265,34 +1212,27 @@ describe("connecting a component to store", () => {
|
|||||||
</templates>
|
</templates>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
function mapStoreToPropsTodoApp(state) {
|
class TodoApp extends ConnectedComponent<any, any, any> {
|
||||||
|
components = { TodoItem };
|
||||||
|
static mapStoreToProps(state) {
|
||||||
return {
|
return {
|
||||||
todos: state.todos
|
todos: state.todos
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class TodoApp extends Component<any, any, any> {
|
|
||||||
components = { ConnectedTodoItem };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectedTodoApp = connect(
|
|
||||||
TodoApp,
|
|
||||||
mapStoreToPropsTodoApp
|
|
||||||
);
|
|
||||||
|
|
||||||
let renderCount = 0;
|
let renderCount = 0;
|
||||||
let fCount = 0;
|
let fCount = 0;
|
||||||
|
|
||||||
function mapStoreToPropsTodoItem(state, ownProps) {
|
class TodoItem extends ConnectedComponent<any, any, any> {
|
||||||
|
state = { isEditing: false };
|
||||||
|
|
||||||
|
static mapStoreToProps(state, ownProps) {
|
||||||
fCount++;
|
fCount++;
|
||||||
return {
|
return {
|
||||||
todo: state.todos[ownProps.id]
|
todo: state.todos[ownProps.id]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class TodoItem extends Component<any, any, any> {
|
|
||||||
state = { isEditing: false };
|
|
||||||
|
|
||||||
removeTodo() {
|
removeTodo() {
|
||||||
this.env.store.commit("removeTodo");
|
this.env.store.commit("removeTodo");
|
||||||
}
|
}
|
||||||
@@ -1302,13 +1242,8 @@ describe("connecting a component to store", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectedTodoItem = connect(
|
|
||||||
TodoItem,
|
|
||||||
mapStoreToPropsTodoItem
|
|
||||||
);
|
|
||||||
|
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new ConnectedTodoApp(env);
|
const app = new TodoApp(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe(
|
expect(fixture.innerHTML).toBe(
|
||||||
@@ -1326,8 +1261,17 @@ describe("connecting a component to store", () => {
|
|||||||
|
|
||||||
test("connected component willpatch/patch hooks are called on store updates", async () => {
|
test("connected component willpatch/patch hooks are called on store updates", async () => {
|
||||||
const steps: string[] = [];
|
const steps: string[] = [];
|
||||||
env.qweb.addTemplate("App", `<div><t t-esc="props.msg"/></div>`);
|
|
||||||
class App extends Component<any, any, any> {
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="App"><t t-esc="props.msg"/></div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
|
||||||
|
class App extends ConnectedComponent<any, any, any> {
|
||||||
|
static mapStoreToProps(s) {
|
||||||
|
return { msg: s.msg };
|
||||||
|
}
|
||||||
willPatch() {
|
willPatch() {
|
||||||
steps.push("willpatch");
|
steps.push("willpatch");
|
||||||
}
|
}
|
||||||
@@ -1335,12 +1279,6 @@ describe("connecting a component to store", () => {
|
|||||||
steps.push("patched");
|
steps.push("patched");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const ConnectedApp = connect(
|
|
||||||
App,
|
|
||||||
function(s) {
|
|
||||||
return { msg: s.msg };
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const state = { msg: "a" };
|
const state = { msg: "a" };
|
||||||
const mutations = {
|
const mutations = {
|
||||||
@@ -1351,7 +1289,7 @@ describe("connecting a component to store", () => {
|
|||||||
|
|
||||||
const store = new Store({ state, mutations });
|
const store = new Store({ state, mutations });
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new ConnectedApp(env);
|
const app = new App(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div>a</div>");
|
expect(fixture.innerHTML).toBe("<div>a</div>");
|
||||||
@@ -1362,29 +1300,4 @@ describe("connecting a component to store", () => {
|
|||||||
expect(steps).toEqual(["willpatch", "patched"]);
|
expect(steps).toEqual(["willpatch", "patched"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("connected component has its own name", () => {
|
|
||||||
function mapStoreToProps() {}
|
|
||||||
|
|
||||||
class Named extends Component<any, any, any> {}
|
|
||||||
const namedConnected = connect(
|
|
||||||
Named,
|
|
||||||
mapStoreToProps
|
|
||||||
);
|
|
||||||
expect(namedConnected.name).toMatch("ConnectedNamed");
|
|
||||||
|
|
||||||
class ParentNamed extends Component<any, any, any> {}
|
|
||||||
class ChildNamed extends ParentNamed {}
|
|
||||||
const childConnected = connect(
|
|
||||||
ChildNamed,
|
|
||||||
mapStoreToProps
|
|
||||||
);
|
|
||||||
expect(childConnected.name).toMatch("ConnectedChildNamed");
|
|
||||||
|
|
||||||
const Anonymous = class extends Component<any, any, any> {};
|
|
||||||
const anonymousConnected = connect(
|
|
||||||
Anonymous,
|
|
||||||
mapStoreToProps
|
|
||||||
);
|
|
||||||
expect(anonymousConnected.name).toMatch(/^Connectedclass_\d+/);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Likes Counter Widget
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
class Counter extends owl.Component {
|
||||||
|
state = { counter: 0 };
|
||||||
|
|
||||||
|
increment() {
|
||||||
|
this.state.counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Message Widget
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
class Message extends owl.Component {
|
||||||
|
components = { Counter };
|
||||||
|
|
||||||
|
shouldUpdate(nextProps) {
|
||||||
|
return nextProps.message !== this.props.message;
|
||||||
|
}
|
||||||
|
removeMessage() {
|
||||||
|
this.trigger("remove-message", {
|
||||||
|
id: this.props.message.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Root Widget
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
class App extends owl.Component {
|
||||||
|
components = { Message };
|
||||||
|
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmark(message, fn, callback) {
|
||||||
|
if (this.state.multipleFlag) {
|
||||||
|
const N = 20;
|
||||||
|
let n = N;
|
||||||
|
let total = 0;
|
||||||
|
let cb = info => {
|
||||||
|
let finalize = () => {
|
||||||
|
n--;
|
||||||
|
total += info.delta;
|
||||||
|
if (n === 0) {
|
||||||
|
const avg = total / N;
|
||||||
|
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._benchmark(message, fn, cb);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.state.clearAfterFlag) {
|
||||||
|
this._benchmark(
|
||||||
|
"clear",
|
||||||
|
() => {
|
||||||
|
this.state.messages = [];
|
||||||
|
},
|
||||||
|
finalize,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
finalize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this._benchmark(message, fn, cb);
|
||||||
|
} else {
|
||||||
|
this._benchmark(message, fn, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_benchmark(message, fn, cb, log = true) {
|
||||||
|
setTimeout(() => {
|
||||||
|
startMeasure(message);
|
||||||
|
fn();
|
||||||
|
stopMeasure(info => {
|
||||||
|
if (log) {
|
||||||
|
this.log(info.msg);
|
||||||
|
}
|
||||||
|
if (cb) {
|
||||||
|
cb(info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessages(n) {
|
||||||
|
this.benchmark("add " + n, () => {
|
||||||
|
const newMessages = buildData(n);
|
||||||
|
this.state.messages.push.apply(this.state.messages, newMessages);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this._benchmark("clear", () => {
|
||||||
|
this.state.messages = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSomeMessages() {
|
||||||
|
this.benchmark("update every 10th", () => {
|
||||||
|
const messages = this.state.messages;
|
||||||
|
for (let i = 0; i < this.state.messages.length; i += 10) {
|
||||||
|
const msg = Object.assign({}, messages[i]);
|
||||||
|
msg.author += "!!!";
|
||||||
|
this.set(messages, i, msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
removeMessage(event) {
|
||||||
|
this.benchmark("remove message", () => {
|
||||||
|
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
||||||
|
this.state.messages.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log(str, isBold) {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
if (isBold) {
|
||||||
|
div.classList.add("bold");
|
||||||
|
}
|
||||||
|
div.textContent = `> ${str}`;
|
||||||
|
this.refs.log.appendChild(div);
|
||||||
|
this.refs.log.scrollTop = this.refs.log.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearLog() {
|
||||||
|
this.refs.log.innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMultiple() {
|
||||||
|
this.state.multipleFlag = !this.state.multipleFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleClear() {
|
||||||
|
this.state.clearAfterFlag = !this.state.clearAfterFlag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Application initialization
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
async function start() {
|
||||||
|
const templates = await owl.utils.loadTemplates("templates.xml");
|
||||||
|
const env = {
|
||||||
|
qweb: new owl.QWeb(templates)
|
||||||
|
};
|
||||||
|
const app = new App(env);
|
||||||
|
app.mount(document.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
start();
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>OWL 0.16.0 Benchmark</title>
|
||||||
|
<link href="../shared/main.css" rel="stylesheet"/>
|
||||||
|
<script src='owl.js'></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id='main'></div>
|
||||||
|
<script src='app.js' type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
|||||||
|
<templates>
|
||||||
|
<div t-name="App" class="main">
|
||||||
|
<div class="left-thing">
|
||||||
|
<div class="title">Actions</div>
|
||||||
|
<div class="panel">
|
||||||
|
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
||||||
|
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
||||||
|
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
||||||
|
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
||||||
|
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
||||||
|
<button t-on-click="clear">Clear</button>
|
||||||
|
</div>
|
||||||
|
<div class="flags">
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
|
||||||
|
<label for="multipleflag">Do it 20x</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
|
||||||
|
<label for="clearFlag">Clear after</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
||||||
|
<hr/>
|
||||||
|
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
||||||
|
<div class="log">
|
||||||
|
<div class="log-content" t-ref="log"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right-thing">
|
||||||
|
<div class="content" t-on-remove-message="removeMessage">
|
||||||
|
<t t-foreach="state.messages" t-as="message">
|
||||||
|
<Message t-key="message.id" message="message"/>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="Message" class="message">
|
||||||
|
<span class="author"><t t-esc="props.message.author"/></span>
|
||||||
|
<span class="msg"><t t-esc="props.message.msg"/></span>
|
||||||
|
<button class="remove" t-on-click="removeMessage">Remove</button>
|
||||||
|
<Counter/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="Counter">
|
||||||
|
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</templates>
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
|
font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||||
Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial,
|
|
||||||
sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
<li><a href="benchmarks/owl-0.13.0">OWL 0.13.0</a></li>
|
<li><a href="benchmarks/owl-0.13.0">OWL 0.13.0</a></li>
|
||||||
<li><a href="benchmarks/owl-0.14.0">OWL 0.14.0</a></li>
|
<li><a href="benchmarks/owl-0.14.0">OWL 0.14.0</a></li>
|
||||||
<li><a href="benchmarks/owl-0.15.0">OWL 0.15.0</a></li>
|
<li><a href="benchmarks/owl-0.15.0">OWL 0.15.0</a></li>
|
||||||
|
<li><a href="benchmarks/owl-0.16.0">OWL 0.16.0</a></li>
|
||||||
<li><a href="benchmarks/owl-master">OWL Master</a></li>
|
<li><a href="benchmarks/owl-master">OWL Master</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
+1
-3
@@ -1,9 +1,7 @@
|
|||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
|
font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||||
Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial,
|
|
||||||
sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title,
|
.title,
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
|
font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||||
Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial,
|
|
||||||
sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -181,10 +179,14 @@ body {
|
|||||||
|
|
||||||
.right-pane .error {
|
.right-pane .error {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-top: 40%;
|
width: 90%;
|
||||||
font-size: 30px;
|
padding-top: 30%;
|
||||||
|
font-size: 18px;
|
||||||
color: darkred;
|
color: darkred;
|
||||||
text-align: center;
|
margin-left: 5%;
|
||||||
padding-left: 30px;
|
}
|
||||||
padding-right: 30px;
|
|
||||||
|
.right-pane .error pre {
|
||||||
|
overflow: auto;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-26
@@ -1,16 +1,17 @@
|
|||||||
const COMPONENTS = `// In this example, we show how components can be defined and created.
|
const COMPONENTS = `// In this example, we show how components can be defined and created.
|
||||||
|
|
||||||
class Counter extends owl.Component {
|
class Greeter extends owl.Component {
|
||||||
state = { value: 0 };
|
state = { word: 'Hello' };
|
||||||
|
|
||||||
increment() {
|
toggle() {
|
||||||
this.state.value++;
|
this.state.word = this.state.word === 'Hi' ? 'Hello' : 'Hi'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main root component
|
// Main root component
|
||||||
class App extends owl.Component {
|
class App extends owl.Component {
|
||||||
components = { Counter };
|
components = { Greeter };
|
||||||
|
state = { name: 'World'};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application setup
|
// Application setup
|
||||||
@@ -21,20 +22,25 @@ app.mount(document.body);
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const COMPONENTS_XML = `<templates>
|
const COMPONENTS_XML = `<templates>
|
||||||
<button t-name="Counter" t-on-click="increment">
|
<div t-name="Greeter" class="greeter" t-on-click="toggle">
|
||||||
Click Me! [<t t-esc="state.value"/>]
|
<t t-esc="state.word"/>, <t t-esc="props.name"/>
|
||||||
</button>
|
</div>
|
||||||
|
|
||||||
<div t-name="App">
|
<div t-name="App">
|
||||||
<Counter />
|
<Greeter name="state.name"/>
|
||||||
<Counter />
|
|
||||||
</div>
|
</div>
|
||||||
</templates>`;
|
</templates>
|
||||||
|
`;
|
||||||
|
|
||||||
const COMPONENTS_CSS = `button {
|
const COMPONENTS_CSS = `.greeter {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
width: 220px;
|
width: 300px;
|
||||||
|
height: 100px;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 100px;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
user-select: none;
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const ANIMATION = `// The goal of this component is to see how the t-transition directive can be
|
const ANIMATION = `// The goal of this component is to see how the t-transition directive can be
|
||||||
@@ -399,16 +405,15 @@ class TodoItem extends owl.Component {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// TodoApp
|
// TodoApp
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
function mapStoreToProps(state) {
|
class TodoApp extends owl.ConnectedComponent {
|
||||||
|
components = { TodoItem };
|
||||||
|
state = { filter: "all" };
|
||||||
|
|
||||||
|
static mapStoreToProps(state) {
|
||||||
return {
|
return {
|
||||||
todos: state.todos
|
todos: state.todos
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class TodoApp extends owl.Component {
|
|
||||||
components = { TodoItem };
|
|
||||||
state = { filter: "all" };
|
|
||||||
|
|
||||||
get visibleTodos() {
|
get visibleTodos() {
|
||||||
let todos = this.props.todos;
|
let todos = this.props.todos;
|
||||||
if (this.state.filter === "active") {
|
if (this.state.filter === "active") {
|
||||||
@@ -456,8 +461,6 @@ class TodoApp extends owl.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectedTodoApp = owl.connect(TodoApp, mapStoreToProps);
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// App Initialization
|
// App Initialization
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -468,7 +471,7 @@ const env = {
|
|||||||
store,
|
store,
|
||||||
dispatch: store.dispatch.bind(store),
|
dispatch: store.dispatch.bind(store),
|
||||||
};
|
};
|
||||||
const app = new ConnectedTodoApp(env);
|
const app = new TodoApp(env);
|
||||||
app.mount(document.body);
|
app.mount(document.body);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -994,17 +997,19 @@ const RESPONSIVE_XML = `<templates>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div t-name="App" class="app" t-att-class="{mobile: env.isMobile, desktop: !env.isMobile}">
|
<div t-name="App" class="app" t-att-class="{mobile: env.isMobile, desktop: !env.isMobile}">
|
||||||
|
<t t-set="maincontent">
|
||||||
|
<FormView />
|
||||||
|
<Chatter />
|
||||||
|
</t>
|
||||||
<Navbar/>
|
<Navbar/>
|
||||||
<ControlPanel/>
|
<ControlPanel/>
|
||||||
<div class="content-wrapper" t-if="!env.isMobile">
|
<div class="content-wrapper" t-if="!env.isMobile">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<FormView />
|
<t t-raw="maincontent"/>
|
||||||
<Chatter />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<t t-else="1">
|
<t t-else="1">
|
||||||
<FormView />
|
<t t-raw="maincontent"/>
|
||||||
<Chatter />
|
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
</templates>
|
</templates>
|
||||||
|
|||||||
@@ -48,7 +48,8 @@
|
|||||||
<div class="note">Note: these examples require a recent browser to work without a transpilation step. </div>
|
<div class="note">Note: these examples require a recent browser to work without a transpilation step. </div>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="state.error" class="error">
|
<div t-if="state.error" class="error">
|
||||||
<t t-esc="state.error"/>
|
<h3>Error</h3>
|
||||||
|
<pre t-esc="state.error"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="content" t-ref="content"/>
|
<div class="content" t-ref="content"/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user