Compare commits

..

10 Commits

Author SHA1 Message Date
Géry Debongnie b63cd4c9d9 [REL] v1.0.0-beta5 2019-12-20 11:26:53 +01:00
Géry Debongnie 953778dc50 [IMP] component/tags: add inline css tag
This add an important feature: defining completely standalone owl
components, with the template/style and javascript code together.

closes #284
2019-12-20 11:20:10 +01:00
Géry Debongnie 4f61d9f1e0 [FIX] component: no crash if empty props
The expression parser properly format an empty string into an empty
string. But then, this empty string was injected in the props object of
a component, and the compiled code looked like this:

let props5 = { val: };

So, in this case, we simply put undefined, since there are no props
value.

closes #587
2019-12-19 14:32:10 +01:00
Sébastien Theys bc2c7edff4 [FIX] store: prevent crash when selector returns null
Because `typeof null === 'object'`.
2019-12-19 14:24:36 +01:00
Géry Debongnie c8d9c0b50e [DOC] add npm install instruction 2019-12-19 14:12:00 +01:00
Géry Debongnie 15b25fd838 [IMP] owl: prepare owl to be released on npm
closes #196
2019-12-19 14:09:41 +01:00
Géry Debongnie 1db0f5ac9b [REL] v1.0.0-beta4 2019-12-17 15:26:49 +01:00
Géry Debongnie 6465665550 [FIX] qweb/component: handle nested t-foreach and components
The way keys were handled in QWeb was mostly ok, but a little naive. It
is fine when we deal with a list of dom nodes, since the reconciliation
algorithm need only to be able to differentiate/reconcile nodes in that
list, but it is an issue with components, which needs to globally be
able to find themselves in their parent's children map.

Because of the way this was handled, there were situations were Owl
internal virtual dom would crash, since components wrongly assumed that
they were already rendered in a different place.

To fix this, we generalize the way keys are generated, by concatenating
all sub keys coming from each iteration loops.

closes #584
2019-12-17 15:23:24 +01:00
Géry Debongnie 9216c5c24b [FIX] qweb: t-call properly transfer key to sub components
Since t-call is now a function call, we need to properly handle the
internal key used to find previous components.  If a t-call is inside a
t-foreach, then we need to transfer the key to the sub template.
Otherwise, each component in the subtemplate will be associated to the
same key, which means that it will lead to big issues: components are
destroyed and reused...

closes #581
2019-12-16 20:30:34 +01:00
Géry Debongnie d9bf4284c6 [REL] v1.0.0-beta3 2019-12-16 09:56:35 +01:00
24 changed files with 1053 additions and 403 deletions
+8 -2
View File
@@ -102,10 +102,16 @@ Submit a PR!
## Installing/Building ## Installing/Building
Owl can be installed with the following command:
```
npm install @odoo/owl
```
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-1.0.0-beta2.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta2/owl.js) - [owl-1.0.0-beta5.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta5/owl.js)
- [owl-1.0.0-beta2.min.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta2/owl.min.js) - [owl-1.0.0-beta5.min.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta5/owl.min.js)
Some npm scripts are available: Some npm scripts are available:
+9 -9
View File
@@ -56,15 +56,15 @@ useState Link
config RouteComponent config RouteComponent
mode Router mode Router
core tags core tags
EventBus xml EventBus css
Observer utils Observer xml
hooks debounce hooks utils
onWillStart escape onWillStart debounce
onMounted loadJS onMounted escape
onWillUpdateProps loadFile onWillUpdateProps loadJS
onWillPatch shallowEqual onWillPatch loadFile
onPatched whenReady onPatched shallowEqual
onWillUnmount onWillUnmount whenReady
useContext useContext
useState useState
useRef useRef
+4
View File
@@ -225,6 +225,10 @@ to be called in the constructor.
} }
``` ```
- **`style`** (string, optional): it should be the return value of the [`css tag](tags.md#css-tag),
which is used to inject stylesheet whenever the component is visible on the
screen.
There is another static property defined on the `Component` class: `current`. There is another static property defined on the `Component` class: `current`.
This property is set to the currently being defined component (in the constructor). This property is set to the currently being defined component (in the constructor).
This is the way [hooks](hooks.md) are able to get a reference to the target This is the way [hooks](hooks.md) are able to get a reference to the target
+88 -4
View File
@@ -4,16 +4,19 @@
- [Overview](#overview) - [Overview](#overview)
- [`xml` tag](#xml-tag) - [`xml` tag](#xml-tag)
- [`css` tag](#css-tag)
## Overview ## Overview
Tags are very small helpers to make it easy to write inline templates. There is Tags are very small helpers intended to make it easy to write inline templates
only one currently available tag: `xml`, but we plan to add other tags later, or styles. There are currently two tags: `css` and `xml`. With these functions,
such as a `css` tag, which will be used to write [single file components](../tooling.md#single-file-component). it is possible to write [single file components](../tooling.md#single-file-component).
## XML tag ## XML tag
Without tags, creating a standalone component would look like this: The `xml` tag is certainly the most useful tag. It is used to define an inline
QWeb template for a component. Without tags, creating a standalone component
would look like this:
```js ```js
import { Component } from 'owl' import { Component } from 'owl'
@@ -52,3 +55,84 @@ class MyComponent extends Component {
... ...
} }
``` ```
## CSS tag
The CSS tag is useful to define a css stylesheet in the javascript file:
```js
class MyComponent extends Component {
static template = xml`
<div class="my-component">some template</div>
`;
static css`
.my-component {
color: red;
}
`;
}
```
The `css` tag registers internally the css information. Then, whenever the first
instance of the component is created, will add a `<style>` tag to the document
`<head>`.
Note that to make it more useful, like other css preprocessors, the `css` tag
accepts a small extension of the css specification: css scopes can be nested,
and the rules will then be expanded by the `css` helper:
```scss
.my-component {
display: block;
.sub-component h {
color: red;
}
}
```
will be formatted as:
```css
.my-component {
display: block;
}
.my-component .sub-component h {
color: red;
}
```
Now, there is no additional processing done by the `css` tag. However, since it
is done in javascript at runtime, we actually have more power. For example:
1. sharing values between javascript and css:
```js
import { theme } from "./theme";
class MyComponent extends Component {
static template = xml`<div class="my-component">...</div>`;
static style = css`
.my-component {
color: ${theme.MAIN_COLOR};
background-color: ${theme.SECONDARY_color};
}
`;
}
```
2. scoping rules to the current component:
```js
import { generateUUID } from "./utils";
const uuid = generateUUID();
class MyComponent extends Component {
static template = xml`<div data-o-${uuid}="">...</div>`;
static style = css`
[data-o-${uuid}] {
color: red;
}
`;
}
```
+20 -9
View File
@@ -43,32 +43,43 @@ useful to compare various performance metrics on some tasks.
It is very useful to group code by feature instead of by type of file. It makes It is very useful to group code by feature instead of by type of file. It makes
it easier to scale application to larger size. it easier to scale application to larger size.
To do so, Owl currently has a small helper that makes it easy to define a To do so, Owl has two small helpers that make it easy to define a
template inside a javascript (or typescript) file: the [`xml`](reference/tags.md#xml-tag) template or a stylesheet inside a javascript (or typescript) file: the
helper. With this, a template is automatically registered to [QWeb](reference/qweb_engine.md). [`xml`](reference/tags.md#xml-tag) and [`css`](reference/tags.md#css-tag)
helper.
This means that the template and the javascript code can be defined in the same This means that the template, the style and the javascript code can be defined in
file. It is not currently possible to add css to the same file, but Owl may the same file. For example:
get a `css` tag helper later.
```js ```js
const { Component } = owl; const { Component } = owl;
const { xml } = owl.tags; const { xml, css } = owl.tags;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// TEMPLATE // TEMPLATE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
const TEMPLATE = xml/* xml */ ` const TEMPLATE = xml/* xml */ `
<div class="main two-columns"> <div class="main">
<Sidebar/> <Sidebar/>
<Content /> <Content />
</div>`; </div>`;
// -----------------------------------------------------------------------------
// STYLE
// -----------------------------------------------------------------------------
const STYLE = css/* css */ `
.main {
display: grid;
grid-template-columns: 200px auto;
}
`;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// CODE // CODE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class MyComponent extends Component { class Main extends Component {
static template = TEMPLATE; static template = TEMPLATE;
static style = STYLE;
static components = { Sidebar, Content }; static components = { Sidebar, Content };
// rest of component... // rest of component...
+6 -3
View File
@@ -1,8 +1,11 @@
{ {
"name": "owl-framework", "name": "@odoo/owl",
"version": "1.0.0-beta2", "version": "1.0.0-beta5",
"description": "Odoo Web Library (OWL)", "description": "Odoo Web Library (OWL)",
"main": "src/index.ts", "main": "dist/owl/index.js",
"types": "dist/owl/index.d.ts",
"prepublish": "npm run build",
"files": ["dist/owl/"],
"engines": { "engines": {
"node": ">=10.15.3" "node": ">=10.15.3"
}, },
+1 -1
View File
@@ -1,6 +1,6 @@
# 🦉 OWL Roadmap 🦉 # 🦉 OWL Roadmap 🦉
- Current version: 1.0.0-beta2 - Current version: 1.0.0-beta5
- Status: mostly stable - Status: mostly stable
This roadmap is only an attempt at predicting Owl's future. Everything may This roadmap is only an attempt at predicting Owl's future. Everything may
+18
View File
@@ -6,6 +6,7 @@ import "./directive";
import { Fiber } from "./fiber"; import { Fiber } from "./fiber";
import "./props_validation"; import "./props_validation";
import { Scheduler, scheduler } from "./scheduler"; import { Scheduler, scheduler } from "./scheduler";
import { activateSheet } from "./styles";
/** /**
* Owl Component System * Owl Component System
@@ -199,6 +200,9 @@ export class Component<T extends Env, Props extends {}> {
refs: null, refs: null,
scope: null scope: null
}; };
if (constr.style) {
this.__applyStyles(constr);
}
} }
/** /**
@@ -580,6 +584,20 @@ export class Component<T extends Env, Props extends {}> {
return fiber; return fiber;
} }
/**
* Apply the stylesheets defined by the component. Note that we need to make
* sure all inherited stylesheets are applied as well. We then delete the
* `style` key from the constructor to make sure we do not apply it again.
*/
private __applyStyles(constr) {
while (constr && constr.style) {
if (constr.hasOwnProperty("style")) {
activateSheet(constr.style, constr.name);
delete constr.style;
}
constr = constr.__proto__;
}
}
__getTemplate(qweb: QWeb): string { __getTemplate(qweb: QWeb): string {
let p = (<any>this).constructor; let p = (<any>this).constructor;
if (!p.hasOwnProperty("_template")) { if (!p.hasOwnProperty("_template")) {
+1 -1
View File
@@ -211,7 +211,7 @@ QWeb.addDirective({
} else if (!name.startsWith("t-")) { } else if (!name.startsWith("t-")) {
if (name !== "class" && name !== "style") { if (name !== "class" && name !== "style") {
// this is a prop! // this is a prop!
props[name] = ctx.formatExpression(value); props[name] = ctx.formatExpression(value) || "undefined";
} }
} }
} }
+56
View File
@@ -0,0 +1,56 @@
/**
* Owl Style System
*
* This files contains the Owl code related to processing (extended) css strings
* and creating/adding <style> tags to the document head.
*/
export const STYLESHEETS: { [id: string]: HTMLStyleElement } = {};
function processSheet(str: string): string {
const tokens = str.split(/(\{|\}|;)/).map(s => s.trim());
const selectorStack: string[] = [];
const parts: string[] = [];
let rules: string[] = [];
function generateRules() {
if (rules.length) {
parts.push(selectorStack.join(" ") + " {");
parts.push(...rules);
parts.push("}");
rules = [];
}
}
while (tokens.length) {
let token = tokens.shift()!;
if (token === "}") {
generateRules();
selectorStack.pop();
} else {
if (tokens[0] === "{") {
generateRules();
selectorStack.push(token);
tokens.shift();
}
if (tokens[0] === ";") {
rules.push(" " + token + ";");
}
}
}
return parts.join("\n");
}
export function registerSheet(id: string, css: string) {
const sheet = document.createElement("style");
sheet.innerHTML = processSheet(css);
STYLESHEETS[id] = sheet;
}
export function activateSheet(id, name) {
const sheet = STYLESHEETS[id];
if (!sheet) {
throw new Error(
`Invalid css stylesheet for component '${name}'. Did you forget to use the 'css' tag helper?`
);
}
sheet.setAttribute("component", name);
document.head.appendChild(sheet);
}
+15 -8
View File
@@ -1,6 +1,7 @@
import { CompilationContext } from "./compilation_context"; import { CompilationContext } from "./compilation_context";
import { QWeb } from "./qweb"; import { QWeb } from "./qweb";
import { htmlToVDOM } from "../vdom/html_to_vdom"; import { htmlToVDOM } from "../vdom/html_to_vdom";
import { QWebVar } from "./expression_parser";
/** /**
* Owl QWeb Directives * Owl QWeb Directives
@@ -119,7 +120,7 @@ QWeb.addDirective({
ctx.rootContext.shouldDefineScope = true; ctx.rootContext.shouldDefineScope = true;
const variable = node.getAttribute("t-set")!; const variable = node.getAttribute("t-set")!;
let value = node.getAttribute("t-value")!; let value = node.getAttribute("t-value")!;
ctx.variables[variable] = ctx.variables[variable] || {}; ctx.variables[variable] = ctx.variables[variable] || ({} as QWebVar);
let qwebvar = ctx.variables[variable]; let qwebvar = ctx.variables[variable];
const hasBody = node.hasChildNodes(); const hasBody = node.hasChildNodes();
@@ -229,7 +230,7 @@ QWeb.addDirective({
// ------------------------------------------------ // ------------------------------------------------
if (!qweb.subTemplates[subTemplate]) { if (!qweb.subTemplates[subTemplate]) {
qweb.subTemplates[subTemplate] = true; qweb.subTemplates[subTemplate] = true;
const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx); const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx, true);
qweb.subTemplates[subTemplate] = subTemplateFn; qweb.subTemplates[subTemplate] = subTemplateFn;
} }
@@ -264,17 +265,16 @@ QWeb.addDirective({
// ------------------------------------------------ // ------------------------------------------------
const callingScope = hasBody ? "scope" : "Object.assign(Object.create(context), scope)"; const callingScope = hasBody ? "scope" : "Object.assign(Object.create(context), scope)";
const parentComponent = `utils.getComponent(context)`; const parentComponent = `utils.getComponent(context)`;
const keyCode = ctx.loopNumber || ctx.hasKey0 ? `, key: ${ctx.generateTemplateKey()}` : "";
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}${keyCode}})`;
if (ctx.parentNode) { if (ctx.parentNode) {
ctx.addLine( ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, Object.assign({}, extra, {parentNode: c${ctx.parentNode}, parent: ${parentComponent}}));`
);
} else { } else {
// this is a t-call with no parentnode, we need to extract the result // this is a t-call with no parentnode, we need to extract the result
ctx.rootContext.shouldDefineResult = true; ctx.rootContext.shouldDefineResult = true;
ctx.addLine(`result = []`); ctx.addLine(`result = []`);
ctx.addLine( ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, Object.assign({}, extra, {parentNode: result, parent: ${parentComponent}}));`
);
ctx.addLine(`result = result[0]`); ctx.addLine(`result = result[0]`);
} }
@@ -334,6 +334,13 @@ QWeb.addDirective({
`Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')` `Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')`
); );
} }
if (nodeCopy.hasAttribute("t-key")) {
const expr = ctx.formatExpression(nodeCopy.getAttribute("t-key")!);
ctx.addLine(`let key${ctx.loopNumber} = ${expr};`);
nodeCopy.removeAttribute("t-key");
} else {
ctx.addLine(`let key${ctx.loopNumber} = i${ctx.loopNumber};`);
}
nodeCopy.removeAttribute("t-foreach"); nodeCopy.removeAttribute("t-foreach");
qweb._compileNode(nodeCopy, ctx); qweb._compileNode(nodeCopy, ctx);
+10 -14
View File
@@ -26,7 +26,8 @@ export class CompilationContext {
templateName: string; templateName: string;
allowMultipleRoots: boolean = false; allowMultipleRoots: boolean = false;
hasParentWidget: boolean = false; hasParentWidget: boolean = false;
currentKey: string = ""; hasKey0: boolean = false;
keyStack: boolean[] = [];
constructor(name?: string) { constructor(name?: string) {
this.rootContext = this; this.rootContext = this;
@@ -48,20 +49,15 @@ export class CompilationContext {
*/ */
generateTemplateKey(prefix: string = ""): string { generateTemplateKey(prefix: string = ""): string {
const id = this.generateID(); const id = this.generateID();
if (this.loopNumber === 0 && !this.currentKey) { if (this.loopNumber === 0 && !this.hasKey0) {
return `'${prefix}__${id}__'`; return `'${prefix}__${id}__'`;
} }
let locationExpr = `\`${prefix}__${id}__`; let key = `\`${prefix}__${id}__`;
for (let i = 0; i < this.loopNumber - 1; i++) { let start = this.hasKey0 ? 0 : 1;
locationExpr += `\${i${i + 1}}__`; for (let i = start; i < this.loopNumber + 1; i++) {
} key += `\${key${i}}__`;
if (this.currentKey) {
const k = this.currentKey;
this.addLine(`let k${id} = ${locationExpr}\` + ${k};`);
} else {
locationExpr += this.loopNumber ? `\${i${this.loopNumber}}__\`` : "`";
this.addLine(`let k${id} = ${locationExpr};`);
} }
this.addLine(`let k${id} = ${key}\`;`);
return `k${id}`; return `k${id}`;
} }
@@ -116,11 +112,11 @@ export class CompilationContext {
} }
indent() { indent() {
this.indentLevel++; this.rootContext.indentLevel++;
} }
dedent() { dedent() {
this.indentLevel--; this.rootContext.indentLevel--;
} }
addLine(line: string): number { addLine(line: string): number {
+15 -4
View File
@@ -303,9 +303,20 @@ QWeb.addDirective({
QWeb.addDirective({ QWeb.addDirective({
name: "key", name: "key",
priority: 45, priority: 45,
atNodeEncounter({ ctx, value }) { atNodeEncounter({ ctx, value, node }) {
let id = ctx.generateID(); if (ctx.loopNumber === 0) {
ctx.addLine(`const nodeKey${id} = ${ctx.formatExpression(value)};`); ctx.keyStack.push(ctx.rootContext.hasKey0);
ctx.currentKey = `nodeKey${id}`; ctx.rootContext.hasKey0 = true;
}
ctx.addLine("{");
ctx.indent();
ctx.addLine(`let key${ctx.loopNumber} = ${ctx.formatExpression(value)};`);
},
finalize({ ctx }) {
ctx.dedent();
ctx.addLine("}");
if (ctx.loopNumber === 0) {
ctx.rootContext.hasKey0 = ctx.keyStack.pop() as boolean;
}
} }
}); });
+15 -6
View File
@@ -383,7 +383,12 @@ export class QWeb extends EventBus {
}); });
} }
_compile(name: string, elem: Element, parentContext?: CompilationContext): CompiledTemplate { _compile(
name: string,
elem: Element,
parentContext?: CompilationContext,
defineKey?: boolean
): CompiledTemplate {
const isDebug = elem.attributes.hasOwnProperty("t-debug"); const isDebug = elem.attributes.hasOwnProperty("t-debug");
const ctx = new CompilationContext(name); const ctx = new CompilationContext(name);
if (elem.tagName !== "t") { if (elem.tagName !== "t") {
@@ -396,6 +401,10 @@ export class QWeb extends EventBus {
ctx.hasParentWidget = true; ctx.hasParentWidget = true;
ctx.shouldDefineResult = false; ctx.shouldDefineResult = false;
ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`); ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`);
if (defineKey) {
ctx.addLine(`let key0 = extra.key || "";`);
ctx.hasKey0 = true;
}
} }
this._compileNode(elem, ctx); this._compileNode(elem, ctx);
@@ -473,9 +482,6 @@ export class QWeb extends EventBus {
} }
return; return;
} }
if (ctx !== ctx.rootContext) {
ctx = ctx.subContext("currentKey", ctx.currentKey);
}
const firstLetter = node.tagName[0]; const firstLetter = node.tagName[0];
if (firstLetter === firstLetter.toUpperCase()) { if (firstLetter === firstLetter.toUpperCase()) {
@@ -750,8 +756,8 @@ export class QWeb extends EventBus {
} }
} }
let nodeID = ctx.generateID(); let nodeID = ctx.generateID();
let nodeKey = ctx.currentKey || nodeID; let key = ctx.loopNumber || ctx.hasKey0 ? `\`\${key${ctx.loopNumber}}_${nodeID}\`` : nodeID;
const parts = [`key:${nodeKey}`]; const parts = [`key:${key}`];
if (attrs.length + tattrs.length > 0) { if (attrs.length + tattrs.length > 0) {
parts.push(`attrs:{${attrs.join(",")}}`); parts.push(`attrs:{${attrs.join(",")}}`);
} }
@@ -780,6 +786,9 @@ export class QWeb extends EventBus {
ctx.addLine(`let vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`); ctx.addLine(`let vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`);
if (ctx.parentNode) { if (ctx.parentNode) {
ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`); ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`);
} else if (ctx.loopNumber || ctx.hasKey0) {
ctx.rootContext.shouldDefineResult = true;
ctx.addLine(`result = vn${nodeID};`);
} }
return nodeID; return nodeID;
+1 -1
View File
@@ -132,7 +132,7 @@ export function useStore(selector, options: SelectorOptions = {}): any {
__destroy.call(component, parent); __destroy.call(component, parent);
}; };
if (typeof result !== "object") { if (typeof result !== "object" || result === null) {
return result; return result;
} }
return new Proxy(result, { return new Proxy(result, {
+17
View File
@@ -1,4 +1,5 @@
import { QWeb } from "./qweb/index"; import { QWeb } from "./qweb/index";
import { registerSheet } from "./component/styles";
/** /**
* Owl Tags * Owl Tags
@@ -25,3 +26,19 @@ export function xml(strings, ...args) {
QWeb.registerTemplate(name, value); QWeb.registerTemplate(name, value);
return name; return name;
} }
/**
* CSS tag helper for defining inline stylesheets. With this, one can simply define
* an inline stylesheet with just the following code:
* ```js
* class A extends Component {
* static style = css`.component-a { color: red; }`;
* }
* ```
*/
export function css(strings, ...args) {
const name = `__sheet__${QWeb.nextId++}`;
const value = String.raw(strings, ...args);
registerSheet(name, value);
return name;
}
@@ -1,5 +1,43 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`basic widget properties can handle empty props 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"__template__2\\"
let utils = this.constructor.utils;
let QWeb = this.constructor;
let parent = context;
let scope = Object.create(context);
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
// Component 'Child'
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
let props2 = {val:undefined};
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w2.destroy();
w2 = false;
}
if (w2) {
w2.__updateProps(props2, extra.fiber, undefined);
let pvnode = w2.__owl__.pvnode;
c1.push(pvnode);
} else {
let componentKey2 = \`Child\`;
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w2 = new W2(parent, props2);
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
c1.push(pvnode);
w2.__owl__.pvnode = pvnode;
}
w2.__owl__.parentLastFiberId = extra.fiber.id;
return vn1;
}"
`;
exports[`basic widget properties reconciliation alg works for t-foreach in t-foreach 1`] = ` exports[`basic widget properties reconciliation alg works for t-foreach in t-foreach 1`] = `
"function anonymous(context, extra "function anonymous(context, extra
) { ) {
@@ -27,6 +65,7 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
scope.section_index = i1 scope.section_index = i1
scope.section = _3[i1] scope.section = _3[i1]
scope.section_value = _4[i1] scope.section_value = _4[i1]
let key1 = i1;
let _6 = scope['section'].blips; let _6 = scope['section'].blips;
if (!_6) { throw new Error('QWeb error: Invalid loop expression')} if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
let _7 = _8 = _6; let _7 = _8 = _6;
@@ -43,8 +82,9 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
scope.blip_index = i2 scope.blip_index = i2
scope.blip = _7[i2] scope.blip = _7[i2]
scope.blip_value = _8[i2] scope.blip_value = _8[i2]
let key2 = i2;
// Component 'Child' // Component 'Child'
let k11 = \`__11__\${i1}__\${i2}__\`; let k11 = \`__11__\${key1}__\${key2}__\`;
let w10 = k11 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k11]] : false; let w10 = k11 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k11]] : false;
let props10 = {blip:scope['blip']}; let props10 = {blip:scope['blip']};
if (w10 && w10.__owl__.currentFiber && !w10.__owl__.vnode) { if (w10 && w10.__owl__.currentFiber && !w10.__owl__.vnode) {
@@ -75,6 +115,81 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
}" }"
`; `;
exports[`basic widget properties same t-keys in two different places 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"__template__2\\"
let utils = this.constructor.utils;
let QWeb = this.constructor;
let parent = context;
let scope = Object.create(context);
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
let c2 = [], p2 = {key:2};
let vn2 = h('div', p2, c2);
c1.push(vn2);
{
let key0 = 1;
// Component 'Child'
let k4 = \`__4__\${key0}__\`;
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false;
let props3 = {blip:'1'};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
w3.destroy();
w3 = false;
}
if (w3) {
w3.__updateProps(props3, extra.fiber, undefined);
let pvnode = w3.__owl__.pvnode;
c2.push(pvnode);
} else {
let componentKey3 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
w3 = new W3(parent, props3);
parent.__owl__.cmap[k4] = w3.__owl__.id;
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
c2.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
w3.__owl__.parentLastFiberId = extra.fiber.id;
}
let c5 = [], p5 = {key:5};
let vn5 = h('div', p5, c5);
c1.push(vn5);
{
let key0 = 1;
// Component 'Child'
let k7 = \`__7__\${key0}__\`;
let w6 = k7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k7]] : false;
let props6 = {blip:'2'};
if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) {
w6.destroy();
w6 = false;
}
if (w6) {
w6.__updateProps(props6, extra.fiber, undefined);
let pvnode = w6.__owl__.pvnode;
c5.push(pvnode);
} else {
let componentKey6 = \`Child\`;
let W6 = context.constructor.components[componentKey6] || QWeb.components[componentKey6]|| scope['Child'];
if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')}
w6 = new W6(parent, props6);
parent.__owl__.cmap[k7] = w6.__owl__.id;
let fiber = w6.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k7, hook: {remove() {},destroy(vn) {w6.destroy();}}});
c5.push(pvnode);
w6.__owl__.pvnode = pvnode;
}
w6.__owl__.parentLastFiberId = extra.fiber.id;
}
return vn1;
}"
`;
exports[`basic widget properties t-key on a component with t-if, and a sibling component 1`] = ` exports[`basic widget properties t-key on a component with t-if, and a sibling component 1`] = `
"function anonymous(context, extra "function anonymous(context, extra
) { ) {
@@ -87,55 +202,57 @@ exports[`basic widget properties t-key on a component with t-if, and a sibling c
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
if (false) { if (false) {
const nodeKey2 = 'str'; {
let key0 = 'str';
// Component 'Child' // Component 'Child'
let k4 = \`__4__\` + nodeKey2; let k3 = \`__3__\${key0}__\`;
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false; let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
let props3 = {}; let props2 = {};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w3.destroy(); w2.destroy();
w3 = false; w2 = false;
} }
if (w3) { if (w2) {
w3.__updateProps(props3, extra.fiber, undefined); w2.__updateProps(props2, extra.fiber, undefined);
let pvnode = w3.__owl__.pvnode; let pvnode = w2.__owl__.pvnode;
c1.push(pvnode); c1.push(pvnode);
} else { } else {
let componentKey3 = \`Child\`; let componentKey2 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['Child']; let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w3 = new W3(parent, props3); w2 = new W2(parent, props2);
parent.__owl__.cmap[k4] = w3.__owl__.id; parent.__owl__.cmap[k3] = w2.__owl__.id;
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w3.__owl__.pvnode = pvnode; w2.__owl__.pvnode = pvnode;
}
w2.__owl__.parentLastFiberId = extra.fiber.id;
} }
w3.__owl__.parentLastFiberId = extra.fiber.id;
} }
// Component 'Child' // Component 'Child'
let w5 = '__6__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__6__']] : false; let w4 = '__5__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__5__']] : false;
let props5 = {}; let props4 = {};
if (w5 && w5.__owl__.currentFiber && !w5.__owl__.vnode) { if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
w5.destroy(); w4.destroy();
w5 = false; w4 = false;
} }
if (w5) { if (w4) {
w5.__updateProps(props5, extra.fiber, undefined); w4.__updateProps(props4, extra.fiber, undefined);
let pvnode = w5.__owl__.pvnode; let pvnode = w4.__owl__.pvnode;
c1.push(pvnode); c1.push(pvnode);
} else { } else {
let componentKey5 = \`Child\`; let componentKey4 = \`Child\`;
let W5 = context.constructor.components[componentKey5] || QWeb.components[componentKey5]|| scope['Child']; let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| scope['Child'];
if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')} if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w5 = new W5(parent, props5); w4 = new W4(parent, props4);
parent.__owl__.cmap['__6__'] = w5.__owl__.id; parent.__owl__.cmap['__5__'] = w4.__owl__.id;
let fiber = w5.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w4.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: '__6__', hook: {remove() {},destroy(vn) {w5.destroy();}}}); let pvnode = h('dummy', {key: '__5__', hook: {remove() {},destroy(vn) {w4.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w5.__owl__.pvnode = pvnode; w4.__owl__.pvnode = pvnode;
} }
w5.__owl__.parentLastFiberId = extra.fiber.id; w4.__owl__.parentLastFiberId = extra.fiber.id;
return vn1; return vn1;
}" }"
`; `;
@@ -322,31 +439,34 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
scope.number_index = i1 scope.number_index = i1
scope.number = _3[i1] scope.number = _3[i1]
scope.number_value = _4[i1] scope.number_value = _4[i1]
const nodeKey6 = scope['number']; let key1 = i1;
{
let key1 = scope['number'];
// Component 'ChildWidget' // Component 'ChildWidget'
let k8 = \`__8__\` + nodeKey6; let k7 = \`__7__\${key1}__\`;
let w7 = k8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k8]] : false; let w6 = k7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k7]] : false;
let props7 = {}; let props6 = {};
if (w7 && w7.__owl__.currentFiber && !w7.__owl__.vnode) { if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) {
w7.destroy(); w6.destroy();
w7 = false; w6 = false;
} }
if (w7) { if (w6) {
w7.__updateProps(props7, extra.fiber, undefined); w6.__updateProps(props6, extra.fiber, undefined);
let pvnode = w7.__owl__.pvnode; let pvnode = w6.__owl__.pvnode;
c1.push(pvnode); c1.push(pvnode);
} else { } else {
let componentKey7 = \`ChildWidget\`; let componentKey6 = \`ChildWidget\`;
let W7 = context.constructor.components[componentKey7] || QWeb.components[componentKey7]|| scope['ChildWidget']; let W6 = context.constructor.components[componentKey6] || QWeb.components[componentKey6]|| scope['ChildWidget'];
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')} if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')}
w7 = new W7(parent, props7); w6 = new W6(parent, props6);
parent.__owl__.cmap[k8] = w7.__owl__.id; parent.__owl__.cmap[k7] = w6.__owl__.id;
let fiber = w7.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w6.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k8, hook: {remove() {},destroy(vn) {w7.destroy();}}}); let pvnode = h('dummy', {key: k7, hook: {remove() {},destroy(vn) {w6.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w7.__owl__.pvnode = pvnode; w6.__owl__.pvnode = pvnode;
}
w6.__owl__.parentLastFiberId = extra.fiber.id;
} }
w7.__owl__.parentLastFiberId = extra.fiber.id;
} }
scope = _origScope5; scope = _origScope5;
return vn1; return vn1;
@@ -922,31 +1042,33 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
let h = this.h; let h = this.h;
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
const nodeKey2 = 'somestring'; {
let key0 = 'somestring';
// Component 'child' // Component 'child'
let k4 = \`__4__\` + nodeKey2; let k3 = \`__3__\${key0}__\`;
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false; let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
let props3 = {flag:scope['state'].flag}; let props2 = {flag:scope['state'].flag};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w3.destroy(); w2.destroy();
w3 = false; w2 = false;
} }
if (w3) { if (w2) {
w3.__updateProps(props3, extra.fiber, undefined); w2.__updateProps(props2, extra.fiber, undefined);
let pvnode = w3.__owl__.pvnode; let pvnode = w2.__owl__.pvnode;
c1.push(pvnode); c1.push(pvnode);
} else { } else {
let componentKey3 = \`child\`; let componentKey2 = \`child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['child']; let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w3 = new W3(parent, props3); w2 = new W2(parent, props2);
parent.__owl__.cmap[k4] = w3.__owl__.id; parent.__owl__.cmap[k3] = w2.__owl__.id;
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w3.__owl__.pvnode = pvnode; w2.__owl__.pvnode = pvnode;
}
w2.__owl__.parentLastFiberId = extra.fiber.id;
} }
w3.__owl__.parentLastFiberId = extra.fiber.id;
return vn1; return vn1;
}" }"
`; `;
@@ -978,32 +1100,35 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
const nodeKey6 = scope['item']; let key1 = i1;
{
let key1 = scope['item'];
// Component 'Child' // Component 'Child'
let k8 = \`__8__\` + nodeKey6; let k7 = \`__7__\${key1}__\`;
let args9 = [scope['item']]; let args8 = [scope['item']];
let w7 = k8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k8]] : false; let w6 = k7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k7]] : false;
let props7 = {}; let props6 = {};
if (w7 && w7.__owl__.currentFiber && !w7.__owl__.vnode) { if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) {
w7.destroy(); w6.destroy();
w7 = false; w6 = false;
} }
if (w7) { if (w6) {
w7.__updateProps(props7, extra.fiber, undefined); w6.__updateProps(props6, extra.fiber, undefined);
let pvnode = w7.__owl__.pvnode; let pvnode = w6.__owl__.pvnode;
c1.push(pvnode); c1.push(pvnode);
} else { } else {
let componentKey7 = \`Child\`; let componentKey6 = \`Child\`;
let W7 = context.constructor.components[componentKey7] || QWeb.components[componentKey7]|| scope['Child']; let W6 = context.constructor.components[componentKey6] || QWeb.components[componentKey6]|| scope['Child'];
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')} if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')}
w7 = new W7(parent, props7); w6 = new W6(parent, props6);
parent.__owl__.cmap[k8] = w7.__owl__.id; parent.__owl__.cmap[k7] = w6.__owl__.id;
let fiber = w7.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['onEv'](...args9, e);});}};}); let fiber = w6.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['onEv'](...args8, e);});}};});
let pvnode = h('dummy', {key: k8, hook: {remove() {},destroy(vn) {w7.destroy();}}}); let pvnode = h('dummy', {key: k7, hook: {remove() {},destroy(vn) {w6.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w7.__owl__.pvnode = pvnode; w6.__owl__.pvnode = pvnode;
}
w6.__owl__.parentLastFiberId = extra.fiber.id;
} }
w7.__owl__.parentLastFiberId = extra.fiber.id;
} }
scope = _origScope5; scope = _origScope5;
return vn1; return vn1;
@@ -1017,11 +1142,13 @@ exports[`t-call handlers are properly bound through a t-call 1`] = `
let utils = this.constructor.utils; let utils = this.constructor.utils;
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let c2 = [], p2 = {key:2,on:{}}; let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`,on:{}};
let vn2 = h('p', p2, c2); let vn2 = h('p', p2, c2);
c1.push(vn2); c1.push(vn2);
extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](e);}; let k3 = \`click__3__\${key0}__\`;
p2.on['click'] = extra.handlers['click__3__']; extra.handlers[k3] = extra.handlers[k3] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](e);};
p2.on['click'] = extra.handlers[k3];
c2.push({text: \`lucas\`}); c2.push({text: \`lucas\`});
}" }"
`; `;
@@ -1034,7 +1161,8 @@ exports[`t-call handlers with arguments are properly bound through a t-call 1`]
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let c2 = [], p2 = {key:2,on:{}}; let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`,on:{}};
let vn2 = h('p', p2, c2); let vn2 = h('p', p2, c2);
c1.push(vn2); c1.push(vn2);
let args3 = [scope['a']]; let args3 = [scope['a']];
@@ -1053,8 +1181,10 @@ exports[`t-call parent is set within t-call 1`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key0 = extra.key || \\"\\";
// Component 'Child' // Component 'Child'
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false; let k3 = \`__3__\${key0}__\`;
let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
let props2 = {}; let props2 = {};
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) { if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w2.destroy(); w2.destroy();
@@ -1069,9 +1199,9 @@ exports[`t-call parent is set within t-call 1`] = `
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child']; let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w2 = new W2(parent, props2); w2 = new W2(parent, props2);
parent.__owl__.cmap['__3__'] = w2.__owl__.id; parent.__owl__.cmap[k3] = w2.__owl__.id;
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w2.__owl__.pvnode = pvnode; w2.__owl__.pvnode = pvnode;
} }
@@ -1089,8 +1219,10 @@ exports[`t-call parent is set within t-call with no parentNode 1`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key0 = extra.key || \\"\\";
// Component 'Child' // Component 'Child'
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false; let k3 = \`__3__\${key0}__\`;
let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
let props2 = {}; let props2 = {};
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) { if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w2.destroy(); w2.destroy();
@@ -1105,9 +1237,9 @@ exports[`t-call parent is set within t-call with no parentNode 1`] = `
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child']; let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w2 = new W2(parent, props2); w2 = new W2(parent, props2);
parent.__owl__.cmap['__3__'] = w2.__owl__.id; parent.__owl__.cmap[k3] = w2.__owl__.id;
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w2.__owl__.pvnode = pvnode; w2.__owl__.pvnode = pvnode;
} }
@@ -1217,16 +1349,16 @@ exports[`t-model directive in a t-foreach 1`] = `
scope.thing_index = i1 scope.thing_index = i1
scope.thing = _3[i1] scope.thing = _3[i1]
scope.thing_value = _4[i1] scope.thing_value = _4[i1]
const nodeKey6 = scope['thing'].id; let key1 = scope['thing'].id;
let _7 = 'checkbox'; let _6 = 'checkbox';
let c8 = [], p8 = {key:nodeKey6,attrs:{type: _7},on:{}}; let c7 = [], p7 = {key:\`\${key1}_7\`,attrs:{type: _6},on:{}};
let vn8 = h('input', p8, c8); let vn7 = h('input', p7, c7);
c1.push(vn8); c1.push(vn7);
let expr8 = scope['thing']; let expr7 = scope['thing'];
let k9 = \`__9__\` + nodeKey6; let k8 = \`__8__\${key1}__\`;
p8.props = {checked: expr8.f}; p7.props = {checked: expr7.f};
extra.handlers[k9] = extra.handlers[k9] || ((ev) => {expr8.f = ev.target.checked}); extra.handlers[k8] = extra.handlers[k8] || ((ev) => {expr7.f = ev.target.checked});
p8.on['input'] = extra.handlers[k9]; p7.on['input'] = extra.handlers[k8];
} }
scope = _origScope5; scope = _origScope5;
return vn1; return vn1;
@@ -1375,6 +1507,36 @@ exports[`t-model directive on an input, type=checkbox 1`] = `
}" }"
`; `;
exports[`t-model directive two inputs in a div with a t-key 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"__template__1\\"
let scope = Object.create(context);
let result;
let h = this.h;
{
let key0 = 'key';
let c1 = [], p1 = {key:\`\${key0}_1\`};
let vn1 = h('div', p1, c1);
result = vn1;
result = vn1;
if (scope['state'].flag) {
let _3 = {'a':true};
let c4 = [], p4 = {key:\`\${key0}_4\`,class:_3};
let vn4 = h('input', p4, c4);
c1.push(vn4);
}
if (!scope['state'].flag) {
let _6 = {'b':true};
let c7 = [], p7 = {key:\`\${key0}_7\`,class:_6};
let vn7 = h('input', p7, c7);
c1.push(vn7);
}
}
return result;
}"
`;
exports[`top level sub widgets basic use 1`] = ` exports[`top level sub widgets basic use 1`] = `
"function anonymous(context, extra "function anonymous(context, extra
) { ) {
@@ -229,14 +229,14 @@ exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
// Template name: \\"Link\\" // Template name: \\"Link\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let _12 = scope['props'].to; let _11 = scope['props'].to;
let c13 = [], p13 = {key:13,attrs:{href: _12}}; let c12 = [], p12 = {key:12,attrs:{href: _11}};
let vn13 = h('a', p13, c13); let vn12 = h('a', p12, c12);
const slot14 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot14) { if (slot13) {
slot14.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c13, parent: extra.parent || context})); slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
} }
return vn13; return vn12;
}" }"
`; `;
@@ -270,35 +270,35 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
scope.user_index = i1 scope.user_index = i1
scope.user = _4[i1] scope.user = _4[i1]
scope.user_value = _5[i1] scope.user_value = _5[i1]
const nodeKey7 = scope['user'].id; let key1 = scope['user'].id;
let c8 = [], p8 = {key:nodeKey7}; let c7 = [], p7 = {key:\`\${key1}_7\`};
let vn8 = h('li', p8, c8); let vn7 = h('li', p7, c7);
c2.push(vn8); c2.push(vn7);
// Component 'Link' // Component 'Link'
let k10 = \`__10__\` + nodeKey7; let k9 = \`__9__\${key1}__\`;
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false; let w8 = k9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k9]] : false;
let props9 = {to:'/user/'+scope['user'].id}; let props8 = {to:'/user/'+scope['user'].id};
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) { if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) {
w9.destroy(); w8.destroy();
w9 = false; w8 = false;
} }
if (w9) { if (w8) {
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope)); w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
let pvnode = w9.__owl__.pvnode; let pvnode = w8.__owl__.pvnode;
c8.push(pvnode); c7.push(pvnode);
} else { } else {
let componentKey9 = \`Link\`; let componentKey8 = \`Link\`;
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link']; let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| scope['Link'];
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')} if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
w9 = new W9(parent, props9); w8 = new W8(parent, props8);
parent.__owl__.cmap[k10] = w9.__owl__.id; parent.__owl__.cmap[k9] = w8.__owl__.id;
w9.__owl__.slotId = 1; w8.__owl__.slotId = 1;
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w8.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}}); let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
c8.push(pvnode); c7.push(pvnode);
w9.__owl__.pvnode = pvnode; w8.__owl__.pvnode = pvnode;
} }
w9.__owl__.parentLastFiberId = extra.fiber.id; w8.__owl__.parentLastFiberId = extra.fiber.id;
} }
scope = _origScope6; scope = _origScope6;
return vn1; return vn1;
@@ -311,11 +311,11 @@ exports[`t-slot directive slots are rendered with proper context, part 2 3`] = `
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c8 = extra.parentNode; let c7 = extra.parentNode;
c8.push({text: \`User \`}); c7.push({text: \`User \`});
let _11 = scope['user'].name; let _10 = scope['user'].name;
if (_11 != null) { if (_10 != null) {
c8.push({text: _11}); c7.push({text: _10});
} }
}" }"
`; `;
@@ -326,14 +326,14 @@ exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
// Template name: \\"Link\\" // Template name: \\"Link\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let _11 = scope['props'].to; let _10 = scope['props'].to;
let c12 = [], p12 = {key:12,attrs:{href: _11}}; let c11 = [], p11 = {key:11,attrs:{href: _10}};
let vn12 = h('a', p12, c12); let vn11 = h('a', p11, c11);
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; const slot12 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot13) { if (slot12) {
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context})); slot12.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c11, parent: extra.parent || context}));
} }
return vn12; return vn11;
}" }"
`; `;
@@ -367,36 +367,36 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
scope.user_index = i1 scope.user_index = i1
scope.user = _4[i1] scope.user = _4[i1]
scope.user_value = _5[i1] scope.user_value = _5[i1]
const nodeKey7 = scope['user'].id; let key1 = scope['user'].id;
let c8 = [], p8 = {key:nodeKey7}; let c7 = [], p7 = {key:\`\${key1}_7\`};
let vn8 = h('li', p8, c8); let vn7 = h('li', p7, c7);
c2.push(vn8); c2.push(vn7);
scope.userdescr = 'User '+scope['user'].name; scope.userdescr = 'User '+scope['user'].name;
// Component 'Link' // Component 'Link'
let k10 = \`__10__\` + nodeKey7; let k9 = \`__9__\${key1}__\`;
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false; let w8 = k9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k9]] : false;
let props9 = {to:'/user/'+scope['user'].id}; let props8 = {to:'/user/'+scope['user'].id};
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) { if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) {
w9.destroy(); w8.destroy();
w9 = false; w8 = false;
} }
if (w9) { if (w8) {
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope)); w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
let pvnode = w9.__owl__.pvnode; let pvnode = w8.__owl__.pvnode;
c8.push(pvnode); c7.push(pvnode);
} else { } else {
let componentKey9 = \`Link\`; let componentKey8 = \`Link\`;
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link']; let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| scope['Link'];
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')} if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
w9 = new W9(parent, props9); w8 = new W8(parent, props8);
parent.__owl__.cmap[k10] = w9.__owl__.id; parent.__owl__.cmap[k9] = w8.__owl__.id;
w9.__owl__.slotId = 1; w8.__owl__.slotId = 1;
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w8.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}}); let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
c8.push(pvnode); c7.push(pvnode);
w9.__owl__.pvnode = pvnode; w8.__owl__.pvnode = pvnode;
} }
w9.__owl__.parentLastFiberId = extra.fiber.id; w8.__owl__.parentLastFiberId = extra.fiber.id;
} }
scope = _origScope6; scope = _origScope6;
return vn1; return vn1;
@@ -409,9 +409,9 @@ exports[`t-slot directive slots are rendered with proper context, part 3 3`] = `
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c8 = extra.parentNode; let c7 = extra.parentNode;
if (scope.userdescr != null) { if (scope.userdescr != null) {
c8.push({text: scope.userdescr}); c7.push({text: scope.userdescr});
} }
}" }"
`; `;
+88
View File
@@ -147,6 +147,21 @@ describe("basic widget properties", () => {
expect(fixture.innerHTML).toBe("<div>1<button>Inc</button></div>"); expect(fixture.innerHTML).toBe("<div>1<button>Inc</button></div>");
}); });
test("can handle empty props", async () => {
class Child extends Component<any, any> {
static template = xml`<span><t t-esc="props.val"/></span>`;
}
class Parent extends Component<any, any> {
static template = xml`<div><Child val=""/></div>`;
static components = { Child };
}
const parent = new Parent();
await parent.mount(fixture);
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
expect(fixture.innerHTML).toBe("<div><span></span></div>");
});
test("cannot be clicked on and updated if not in DOM", async () => { test("cannot be clicked on and updated if not in DOM", async () => {
class Counter extends Component<any, any> { class Counter extends Component<any, any> {
static template = xml` static template = xml`
@@ -322,6 +337,36 @@ describe("basic widget properties", () => {
console.warn = warn; console.warn = warn;
}); });
test("reconciliation alg works for t-foreach in t-foreach, 2", async () => {
class Child extends Component<any, any> {
static template = xml`<div><t t-esc="props.row + '_' + props.col"/></div>`;
}
class Parent extends Component<any, any> {
static template = xml`
<div>
<p t-foreach="state.rows" t-as="row" t-key="row">
<p t-foreach="state.cols" t-as="col" t-key="col">
<Child row="row" col="col"/>
</p>
</p>
</div>`;
static components = { Child };
state = useState({ rows: [1, 2], cols: ["a", "b"] });
}
const widget = new Parent();
await widget.mount(fixture);
expect(fixture.innerHTML).toBe(
"<div><p><p><div>1_a</div></p><p><div>1_b</div></p></p><p><p><div>2_a</div></p><p><div>2_b</div></p></p></div>"
);
widget.state.rows = [2, 1];
await nextTick();
expect(fixture.innerHTML).toBe(
"<div><p><p><div>2_a</div></p><p><div>2_b</div></p></p><p><p><div>1_a</div></p><p><div>1_b</div></p></p></div>"
);
});
test("same t-keys in two different places", async () => { test("same t-keys in two different places", async () => {
class Child extends Component<any, any> { class Child extends Component<any, any> {
static template = xml`<span><t t-esc="props.blip"/></span>`; static template = xml`<span><t t-esc="props.blip"/></span>`;
@@ -339,6 +384,7 @@ describe("basic widget properties", () => {
const widget = new Parent(); const widget = new Parent();
await widget.mount(fixture); await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div><span>1</span></div><div><span>2</span></div></div>"); expect(fixture.innerHTML).toBe("<div><div><span>1</span></div><div><span>2</span></div></div>");
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
}); });
test("t-key on a component with t-if, and a sibling component", async () => { test("t-key on a component with t-if, and a sibling component", async () => {
@@ -4418,6 +4464,28 @@ describe("t-model directive", () => {
expect(comp.state[2].f).toBe(false); expect(comp.state[2].f).toBe(false);
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot(); expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
}); });
test("two inputs in a div with a t-key", async () => {
class SomeComponent extends Component<any, any> {
static template = xml`
<div t-key="'key'">
<input class="a" t-if="state.flag"/>
<input class="b" t-if="!state.flag"/>
</div>
`;
state = useState({ flag: true });
}
const comp = new SomeComponent();
await comp.mount(fixture);
expect(fixture.innerHTML).toBe('<div><input class="a"></div>');
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
fixture.querySelector("input")!.value = "asdf";
expect(fixture.querySelector("input")!.value).toBe("asdf");
comp.state.flag = false;
await nextTick();
expect(fixture.innerHTML).toBe('<div><input class="b"></div>');
expect(fixture.querySelector("input")!.value).toBe("");
});
}); });
describe("environment and plugins", () => { describe("environment and plugins", () => {
@@ -5599,6 +5667,26 @@ describe("t-call", () => {
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot(); expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
}); });
test("t-call in t-foreach and children component", async () => {
env.qweb.addTemplate("sub", `<Child val="val"/>`);
class Child extends Component<any, any> {
static template = xml`<span><t t-esc="props.val"/></span>`;
}
class Parent extends Component<any, any> {
static components = { Child };
static template = xml`
<div>
<t t-foreach="['a', 'b', 'c']" t-as="val" t-key="val">
<t t-call="sub"/>
</t>
</div>`;
}
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>a</span><span>b</span><span>c</span></div>");
});
test("parent is set within t-call with no parentNode", async () => { test("parent is set within t-call with no parentNode", async () => {
env.qweb.addTemplate("sub", `<Child/>`); env.qweb.addTemplate("sub", `<Child/>`);
let child; let child;
-7
View File
@@ -19,13 +19,6 @@ let env: Env;
beforeEach(() => { beforeEach(() => {
fixture = makeTestFixture(); fixture = makeTestFixture();
env = makeTestEnv(); env = makeTestEnv();
env.qweb.addTemplate("Component<any,any>", "<div></div>");
env.qweb.addTemplate(
"Counter",
`<div><t t-esc="state.counter"/><button t-on-click="inc">Inc</button></div>`
);
env.qweb.addTemplate("WidgetA", `<div>Hello<t t-component="b"/></div>`);
env.qweb.addTemplate("WidgetB", `<div>world</div>`);
Component.env = env; Component.env = env;
}); });
+132
View File
@@ -0,0 +1,132 @@
import { Component, Env } from "../../src/component/component";
import { xml, css } from "../../src/tags";
import { makeTestFixture, makeTestEnv } from "../helpers";
//------------------------------------------------------------------------------
// Setup and helpers
//------------------------------------------------------------------------------
// We create before each test:
// - fixture: a div, appended to the DOM, intended to be the target of dom
// manipulations. Note that it is removed after each test.
// - env: an Env, necessary to create new components
let fixture: HTMLElement;
let env: Env;
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
Component.env = env;
document.head.innerHTML = "";
});
afterEach(() => {
fixture.remove();
});
//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------
describe("styles and component", () => {
test("can define an inline stylesheet", async () => {
class App extends Component<any, any> {
static template = xml`<div class="app">text</div>`;
static style = css`
.app {
color: red;
}
`;
}
expect(document.head.innerHTML).toBe("");
const app = new App();
expect(document.head.innerHTML).toBe(`<style component=\"App\">.app {
color: red;
}</style>`);
await app.mount(fixture);
const style = getComputedStyle(app.el!);
expect(style.color).toBe("red");
expect(fixture.innerHTML).toBe('<div class="app">text</div>');
});
test("inherited components properly apply css", async () => {
class App extends Component<any, any> {
static template = xml`<div class="app">text</div>`;
static style = css`
.app {
color: red;
}
`;
}
class SubApp extends App {
static style = css`
.app {
font-weight: bold;
}
`;
}
expect(document.head.innerHTML).toBe("");
const app = new SubApp();
expect(document.head.innerHTML).toBe(`<style component=\"SubApp\">.app {
font-weight: bold;
}</style><style component=\"App\">.app {
color: red;
}</style>`);
await app.mount(fixture);
const style = getComputedStyle(app.el!);
expect(style.color).toBe("red");
expect(style.fontWeight).toBe("bold");
expect(fixture.innerHTML).toBe('<div class="app">text</div>');
});
test("get a meaningful error message if css helper is missing", async () => {
class App extends Component<any, any> {
static template = xml`<div class="app">text</div>`;
static style = `.app {color: red;}`;
}
let error;
try {
new App();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe(
"Invalid css stylesheet for component 'App'. Did you forget to use the 'css' tag helper?"
);
});
test("inline stylesheets are processed", async () => {
class App extends Component<any, any> {
static template = xml`<div class="app">text</div>`;
static style = css`
.app {
color: red;
.some-class {
font-weight: bold;
width: 40px;
}
display: block;
}
`;
}
new App();
expect(document.head.querySelector("style")!.innerHTML).toBe(`.app {
color: red;
}
.app .some-class {
font-weight: bold;
width: 40px;
}
.app {
display: block;
}`);
});
});
+133 -96
View File
@@ -428,6 +428,7 @@ exports[`foreach does not pollute the rendering context 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
let key1 = i1;
let _6 = scope['item']; let _6 = scope['item'];
if (_6 != null) { if (_6 != null) {
c1.push({text: _6}); c1.push({text: _6});
@@ -462,13 +463,13 @@ exports[`foreach iterate on items (on a element node) 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
const nodeKey6 = scope['item']; let key1 = scope['item'];
let c7 = [], p7 = {key:nodeKey6}; let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn7 = h('span', p7, c7); let vn6 = h('span', p6, c6);
c1.push(vn7); c1.push(vn6);
let _8 = scope['item']; let _7 = scope['item'];
if (_8 != null) { if (_7 != null) {
c7.push({text: _8}); c6.push({text: _7});
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -500,6 +501,7 @@ exports[`foreach iterate on items 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
let key1 = i1;
c1.push({text: \` [\`}); c1.push({text: \` [\`});
let _6 = scope['item_index']; let _6 = scope['item_index'];
if (_6 != null) { if (_6 != null) {
@@ -546,6 +548,7 @@ exports[`foreach iterate, dict param 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
let key1 = i1;
c1.push({text: \` [\`}); c1.push({text: \` [\`});
let _6 = scope['item_index']; let _6 = scope['item_index'];
if (_6 != null) { if (_6 != null) {
@@ -592,6 +595,7 @@ exports[`foreach iterate, position 1`] = `
scope.elem_index = i1 scope.elem_index = i1
scope.elem = _3[i1] scope.elem = _3[i1]
scope.elem_value = _4[i1] scope.elem_value = _4[i1]
let key1 = i1;
c1.push({text: \` -\`}); c1.push({text: \` -\`});
if (scope['elem_first']) { if (scope['elem_first']) {
c1.push({text: \` first\`}); c1.push({text: \` first\`});
@@ -635,6 +639,7 @@ exports[`foreach t-foreach in t-forach 1`] = `
scope.number_index = i1 scope.number_index = i1
scope.number = _3[i1] scope.number = _3[i1]
scope.number_value = _4[i1] scope.number_value = _4[i1]
let key1 = i1;
let _6 = scope['letters']; let _6 = scope['letters'];
if (!_6) { throw new Error('QWeb error: Invalid loop expression')} if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
let _7 = _8 = _6; let _7 = _8 = _6;
@@ -651,6 +656,7 @@ exports[`foreach t-foreach in t-forach 1`] = `
scope.letter_index = i2 scope.letter_index = i2
scope.letter = _7[i2] scope.letter = _7[i2]
scope.letter_value = _8[i2] scope.letter_value = _8[i2]
let key2 = i2;
c1.push({text: \` [\`}); c1.push({text: \` [\`});
let _10 = scope['number']; let _10 = scope['number'];
if (_10 != null) { if (_10 != null) {
@@ -693,7 +699,8 @@ exports[`foreach warn if no key in some case 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
let c6 = [], p6 = {key:6}; let key1 = i1;
let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn6 = h('span', p6, c6); let vn6 = h('span', p6, c6);
c1.push(vn6); c1.push(vn6);
let _7 = scope['item']; let _7 = scope['item'];
@@ -757,7 +764,8 @@ exports[`misc global 1`] = `
scope.value_index = i1 scope.value_index = i1
scope.value = _3[i1] scope.value = _3[i1]
scope.value_value = _4[i1] scope.value_value = _4[i1]
let c6 = [], p6 = {key:6}; let key1 = i1;
let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn6 = h('span', p6, c6); let vn6 = h('span', p6, c6);
c1.push(vn6); c1.push(vn6);
let _7 = scope['value']; let _7 = scope['value'];
@@ -777,15 +785,19 @@ exports[`misc global 1`] = `
scope.foo = 'aaa'; scope.foo = 'aaa';
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context)})); let k14 = \`__14__\${key1}__\`;
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k14}));
scope = _origScope13; scope = _origScope13;
} }
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context)})); let k15 = \`__15__\${key1}__\`;
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k15}));
scope.foo = 'bbb'; scope.foo = 'bbb';
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context)})); let k16 = \`__16__\${key1}__\`;
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k16}));
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['_callee-asc'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); let k17 = \`__17__\${key1}__\`;
this.subTemplates['_callee-asc'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k17}));
scope = _origScope10; scope = _origScope10;
} }
} }
@@ -1040,7 +1052,8 @@ exports[`t-call (template calling basic caller 2`] = `
// Template name: \\"_basic-callee\\" // Template name: \\"_basic-callee\\"
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let c2 = [], p2 = {key:2}; let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('span', p2, c2); let vn2 = h('span', p2, c2);
c1.push(vn2); c1.push(vn2);
c2.push({text: \`ok\`}); c2.push({text: \`ok\`});
@@ -1068,7 +1081,8 @@ exports[`t-call (template calling basic caller, no parent node 2`] = `
// Template name: \\"_basic-callee\\" // Template name: \\"_basic-callee\\"
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let c2 = [], p2 = {key:2}; let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('div', p2, c2); let vn2 = h('div', p2, c2);
c1.push(vn2); c1.push(vn2);
c2.push({text: \`ok\`}); c2.push({text: \`ok\`});
@@ -1115,7 +1129,8 @@ exports[`t-call (template calling call with several sub nodes on same line 2`] =
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let c2 = [], p2 = {key:2}; let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('div', p2, c2); let vn2 = h('div', p2, c2);
c1.push(vn2); c1.push(vn2);
c2.push(...scope[utils.zero]); c2.push(...scope[utils.zero]);
@@ -1132,23 +1147,23 @@ exports[`t-call (template calling cascading t-call t-raw='0' 1`] = `
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
{ {
let _origScope10 = scope; let _origScope12 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
let c11 = [], p11 = {key:11}; let c13 = [], p13 = {key:13};
let vn11 = h('span', p11, c11); let vn13 = h('span', p13, c13);
c__0.push(vn11); c__0.push(vn13);
c11.push({text: \`hey\`}); c13.push({text: \`hey\`});
c__0.push({text: \` \`}); c__0.push({text: \` \`});
let c12 = [], p12 = {key:12}; let c14 = [], p14 = {key:14};
let vn12 = h('span', p12, c12); let vn14 = h('span', p14, c14);
c__0.push(vn12); c__0.push(vn14);
c12.push({text: \`yay\`}); c14.push({text: \`yay\`});
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope10; scope = _origScope12;
} }
return vn1; return vn1;
}" }"
@@ -1197,15 +1212,17 @@ exports[`t-call (template calling recursive template, part 1 2`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let c3 = [], p3 = {key:3}; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:\`\${key0}_3\`};
let vn3 = h('div', p3, c3); let vn3 = h('div', p3, c3);
c1.push(vn3); c1.push(vn3);
let c4 = [], p4 = {key:4}; let c4 = [], p4 = {key:\`\${key0}_4\`};
let vn4 = h('span', p4, c4); let vn4 = h('span', p4, c4);
c3.push(vn4); c3.push(vn4);
c4.push({text: \`hey\`}); c4.push({text: \`hey\`});
if (false) { if (false) {
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context)})); let k5 = \`__5__\${key0}__\`;
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k5}));
} }
}" }"
`; `;
@@ -1220,7 +1237,7 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
{ {
let _origScope10 = scope; let _origScope11 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1228,7 +1245,7 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope10; scope = _origScope11;
} }
return vn1; return vn1;
}" }"
@@ -1242,10 +1259,11 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let c2 = [], p2 = {key:2}; let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('div', p2, c2); let vn2 = h('div', p2, c2);
c1.push(vn2); c1.push(vn2);
let c3 = [], p3 = {key:3}; let c3 = [], p3 = {key:\`\${key0}_3\`};
let vn3 = h('p', p3, c3); let vn3 = h('p', p3, c3);
c2.push(vn3); c2.push(vn3);
let _4 = scope['node'].val; let _4 = scope['node'].val;
@@ -1268,6 +1286,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
scope.subtree_index = i1 scope.subtree_index = i1
scope.subtree = _6[i1] scope.subtree = _6[i1]
scope.subtree_value = _7[i1] scope.subtree_value = _7[i1]
let key1 = i1;
{ {
let _origScope9 = scope; let _origScope9 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
@@ -1276,7 +1295,8 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
scope.node = scope['subtree']; scope.node = scope['subtree'];
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context)})); let k10 = \`__10__\${key0}__\${key1}__\`;
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
scope = _origScope9; scope = _origScope9;
} }
} }
@@ -1294,7 +1314,7 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
{ {
let _origScope10 = scope; let _origScope11 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1302,7 +1322,7 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope10; scope = _origScope11;
} }
return vn1; return vn1;
}" }"
@@ -1316,10 +1336,11 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let c2 = [], p2 = {key:2}; let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('div', p2, c2); let vn2 = h('div', p2, c2);
c1.push(vn2); c1.push(vn2);
let c3 = [], p3 = {key:3}; let c3 = [], p3 = {key:\`\${key0}_3\`};
let vn3 = h('p', p3, c3); let vn3 = h('p', p3, c3);
c2.push(vn3); c2.push(vn3);
let _4 = scope['node'].val; let _4 = scope['node'].val;
@@ -1342,6 +1363,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
scope.subtree_index = i1 scope.subtree_index = i1
scope.subtree = _6[i1] scope.subtree = _6[i1]
scope.subtree_value = _7[i1] scope.subtree_value = _7[i1]
let key1 = i1;
{ {
let _origScope9 = scope; let _origScope9 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
@@ -1350,7 +1372,8 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
scope.node = scope['subtree']; scope.node = scope['subtree'];
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context)})); let k10 = \`__10__\${key0}__\${key1}__\`;
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
scope = _origScope9; scope = _origScope9;
} }
} }
@@ -1407,7 +1430,8 @@ exports[`t-call (template calling t-call with t-if 2`] = `
// Template name: \\"sub\\" // Template name: \\"sub\\"
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let c2 = [], p2 = {key:2}; let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('span', p2, c2); let vn2 = h('span', p2, c2);
c1.push(vn2); c1.push(vn2);
c2.push({text: \`ok\`}); c2.push({text: \`ok\`});
@@ -1439,6 +1463,7 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
scope.v_index = i1 scope.v_index = i1
scope.v = _3[i1] scope.v = _3[i1]
scope.v_value = _4[i1] scope.v_value = _4[i1]
let key1 = i1;
scope.val = scope['v'].val; scope.val = scope['v'].val;
{ {
let _origScope8 = scope; let _origScope8 = scope;
@@ -1448,7 +1473,8 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
scope.val3 = scope.val*3; scope.val3 = scope.val*3;
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['sub'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); let k9 = \`__9__\${key1}__\`;
this.subTemplates['sub'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k9}));
scope = _origScope8; scope = _origScope8;
} }
} }
@@ -2096,15 +2122,20 @@ exports[`t-key can use t-key directive on a node 1`] = `
) { ) {
// Template name: \\"test\\" // Template name: \\"test\\"
let scope = Object.create(context); let scope = Object.create(context);
let result;
let h = this.h; let h = this.h;
const nodeKey1 = scope['beer'].id; {
let c2 = [], p2 = {key:nodeKey1}; let key0 = scope['beer'].id;
let vn2 = h('div', p2, c2); let c1 = [], p1 = {key:\`\${key0}_1\`};
let _3 = scope['beer'].name; let vn1 = h('div', p1, c1);
if (_3 != null) { result = vn1;
c2.push({text: _3}); result = vn1;
let _2 = scope['beer'].name;
if (_2 != null) {
c1.push({text: _2});
} }
return vn2; }
return result;
}" }"
`; `;
@@ -2132,13 +2163,13 @@ exports[`t-key t-key directive in a list 1`] = `
scope.beer_index = i1 scope.beer_index = i1
scope.beer = _3[i1] scope.beer = _3[i1]
scope.beer_value = _4[i1] scope.beer_value = _4[i1]
const nodeKey6 = scope['beer'].id; let key1 = scope['beer'].id;
let c7 = [], p7 = {key:nodeKey6}; let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn7 = h('li', p7, c7); let vn6 = h('li', p6, c6);
c1.push(vn7); c1.push(vn6);
let _8 = scope['beer'].name; let _7 = scope['beer'].name;
if (_8 != null) { if (_7 != null) {
c7.push({text: _8}); c6.push({text: _7});
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -2236,16 +2267,16 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
scope.action_index = i1 scope.action_index = i1
scope.action = _3[i1] scope.action = _3[i1]
scope.action_value = _4[i1] scope.action_value = _4[i1]
const nodeKey6 = scope['action_index']; let key1 = scope['action_index'];
let c7 = [], p7 = {key:nodeKey6}; let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn7 = h('li', p7, c7); let vn6 = h('li', p6, c6);
c1.push(vn7); c1.push(vn6);
let c8 = [], p8 = {key:nodeKey6,on:{}}; let c7 = [], p7 = {key:\`\${key1}_7\`,on:{}};
let vn8 = h('a', p8, c8); let vn7 = h('a', p7, c7);
c7.push(vn8); c6.push(vn7);
let args9 = [scope['action']]; let args8 = [scope['action']];
p8.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['activate'](...args9, e);}; p7.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['activate'](...args8, e);};
c8.push({text: \`link\`}); c7.push({text: \`link\`});
} }
scope = _origScope5; scope = _origScope5;
return vn1; return vn1;
@@ -2493,17 +2524,20 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = `
scope.project_index = i1 scope.project_index = i1
scope.project = _3[i1] scope.project = _3[i1]
scope.project_value = _4[i1] scope.project_value = _4[i1]
const nodeKey6 = scope['project']; let key1 = i1;
let _7 = '#'; {
let c8 = [], p8 = {key:nodeKey6,attrs:{href: _7},on:{}}; let key1 = scope['project'];
let vn8 = h('a', p8, c8); let _6 = '#';
c1.push(vn8); let c7 = [], p7 = {key:\`\${key1}_7\`,attrs:{href: _6},on:{}};
let args9 = [scope['project'].id]; let vn7 = h('a', p7, c7);
p8.on['click'] = function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();utils.getComponent(context)['onEdit'](...args9, e);}; c1.push(vn7);
c8.push({text: \` Edit \`}); let args8 = [scope['project'].id];
let _10 = scope['project'].name; p7.on['click'] = function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();utils.getComponent(context)['onEdit'](...args8, e);};
if (_10 != null) { c7.push({text: \` Edit \`});
c8.push({text: _10}); let _9 = scope['project'].name;
if (_9 != null) {
c7.push({text: _9});
}
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -2736,22 +2770,25 @@ exports[`t-ref refs in a loop 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
const nodeKey6 = scope['item']; let key1 = i1;
let c7 = [], p7 = {key:nodeKey6}; {
let vn7 = h('div', p7, c7); let key1 = scope['item'];
c1.push(vn7); let c6 = [], p6 = {key:\`\${key1}_6\`};
const ref8 = (scope['item']); let vn6 = h('div', p6, c6);
p7.hook = { c1.push(vn6);
const ref7 = (scope['item']);
p6.hook = {
create: (_, n) => { create: (_, n) => {
context.__owl__.refs[ref8] = n.elm; context.__owl__.refs[ref7] = n.elm;
}, },
destroy: () => { destroy: () => {
delete context.__owl__.refs[ref8]; delete context.__owl__.refs[ref7];
}, },
}; };
let _9 = scope['item']; let _8 = scope['item'];
if (_9 != null) { if (_8 != null) {
c7.push({text: _9}); c6.push({text: _8});
}
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -2974,16 +3011,16 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
scope.elem_index = i1 scope.elem_index = i1
scope.elem = _3[i1] scope.elem = _3[i1]
scope.elem_value = _4[i1] scope.elem_value = _4[i1]
const nodeKey6 = scope['elem_index']; let key1 = scope['elem_index'];
let c7 = [], p7 = {key:nodeKey6}; let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn7 = h('div', p7, c7); let vn6 = h('div', p6, c6);
c1.push(vn7); c1.push(vn6);
let c8 = [], p8 = {key:nodeKey6}; let c7 = [], p7 = {key:\`\${key1}_7\`};
let vn8 = h('span', p8, c8); let vn7 = h('span', p7, c7);
c7.push(vn8); c6.push(vn7);
c8.push({text: \`v\`}); c7.push({text: \`v\`});
if (scope.v != null) { if (scope.v != null) {
c8.push({text: scope.v}); c7.push({text: scope.v});
} }
scope.v = scope['elem']; scope.v = scope['elem'];
} }
@@ -11,33 +11,35 @@ exports[`RouteComponent can render simple cases 1`] = `
let result; let result;
let h = this.h; let h = this.h;
if (scope['routeComponent']) { if (scope['routeComponent']) {
const nodeKey4 = scope['env'].router.currentRouteName; {
let key0 = scope['env'].router.currentRouteName;
// Component 'routeComponent' // Component 'routeComponent'
let k6 = \`__6__\` + nodeKey4; let k5 = \`__5__\${key0}__\`;
let w5 = k6 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k6]] : false; let w4 = k5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k5]] : false;
let vn7 = {}; let vn6 = {};
result = vn7; result = vn6;
let props5 = Object.assign({}, scope['env'].router.currentParams); let props4 = Object.assign({}, scope['env'].router.currentParams);
if (w5 && w5.__owl__.currentFiber && !w5.__owl__.vnode) { if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
w5.destroy(); w4.destroy();
w5 = false; w4 = false;
} }
if (w5) { if (w4) {
w5.__updateProps(props5, extra.fiber, undefined); w4.__updateProps(props4, extra.fiber, undefined);
let pvnode = w5.__owl__.pvnode; let pvnode = w4.__owl__.pvnode;
utils.defineProxy(vn7, pvnode); utils.defineProxy(vn6, pvnode);
} else { } else {
let componentKey5 = \`routeComponent\`; let componentKey4 = \`routeComponent\`;
let W5 = context.constructor.components[componentKey5] || QWeb.components[componentKey5]|| scope['routeComponent']; let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| scope['routeComponent'];
if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')} if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w5 = new W5(parent, props5); w4 = new W4(parent, props4);
parent.__owl__.cmap[k6] = w5.__owl__.id; parent.__owl__.cmap[k5] = w4.__owl__.id;
let fiber = w5.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w4.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k6, hook: {remove() {},destroy(vn) {w5.destroy();}}}); let pvnode = h('dummy', {key: k5, hook: {remove() {},destroy(vn) {w4.destroy();}}});
utils.defineProxy(vn7, pvnode); utils.defineProxy(vn6, pvnode);
w5.__owl__.pvnode = pvnode; w4.__owl__.pvnode = pvnode;
}
w4.__owl__.parentLastFiberId = extra.fiber.id;
} }
w5.__owl__.parentLastFiberId = extra.fiber.id;
} }
return result; return result;
}" }"
+15 -1
View File
@@ -49,10 +49,13 @@ describe("connecting a component to store", () => {
}); });
test("useStore can observe primitive types and call onUpdate", async () => { test("useStore can observe primitive types and call onUpdate", async () => {
const state = { isBoolean: false }; const state = { isBoolean: false, nullValue: null };
const actions = { const actions = {
setTrue({ state }) { setTrue({ state }) {
state.isBoolean = true; state.isBoolean = true;
},
setNotNull({ state }) {
state.nullValue = "ok";
} }
}; };
const store = new Store({ state, actions }); const store = new Store({ state, actions });
@@ -61,8 +64,10 @@ describe("connecting a component to store", () => {
static template = xml` static template = xml`
<div> <div>
<span t-if="isBoolean">ok</span> <span t-if="isBoolean">ok</span>
<span t-if="nullValue !== null">not null</span>
</div>`; </div>`;
isBoolean: boolean; isBoolean: boolean;
nullValue: string;
constructor() { constructor() {
super(); super();
this.isBoolean = useStore(state => state.isBoolean, { this.isBoolean = useStore(state => state.isBoolean, {
@@ -70,6 +75,11 @@ describe("connecting a component to store", () => {
this.isBoolean = isBoolean; this.isBoolean = isBoolean;
} }
}); });
this.nullValue = useStore(state => state.nullValue, {
onUpdate: nullValue => {
this.nullValue = nullValue;
}
});
} }
} }
@@ -82,6 +92,10 @@ describe("connecting a component to store", () => {
store.dispatch("setTrue"); store.dispatch("setTrue");
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe("<div><span>ok</span></div>"); expect(fixture.innerHTML).toBe("<div><span>ok</span></div>");
store.dispatch("setNotNull");
await nextTick();
expect(fixture.innerHTML).toBe("<div><span>ok</span><span>not null</span></div>");
}); });
test("map works on the result of useStore when the resulting array changes for a bigger one", async () => { test("map works on the result of useStore when the resulting array changes for a bigger one", async () => {