mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fad164c79 | |||
| 53a8841914 | |||
| 19f0863aa3 | |||
| 28f7b44f9e | |||
| fe46a6ccee | |||
| 3c23a7b246 | |||
| c52799de2c | |||
| 4edb90f44b | |||
| 0c1a7aabae | |||
| cfdce29ce7 | |||
| b344d73e07 | |||
| 4cd08d25c8 | |||
| f9861040c8 | |||
| 40d8090232 | |||
| 55be6437f1 | |||
| 6ff4dd74bf | |||
| 3628b49c37 |
@@ -14,6 +14,44 @@ related projects. OWL's main features are:
|
||||
|
||||
**Try it online!** An online playground is available at [https://odoo.github.io/owl/playground](https://odoo.github.io/owl/playground) to let you experiment with the OWL framework.
|
||||
|
||||
## Example
|
||||
|
||||
Here is a short example to illustrate interactive components:
|
||||
|
||||
```xml
|
||||
<templates>
|
||||
<button t-name="Counter" t-on-click="increment">
|
||||
Click Me! [<t t-esc="state.value"/>]
|
||||
</button>
|
||||
<div t-name="App">
|
||||
<span>Hello Owl</span>
|
||||
<Counter />
|
||||
</div>
|
||||
</templates>
|
||||
```
|
||||
|
||||
```javascript
|
||||
class Counter extends owl.Component {
|
||||
state = { value: 0 };
|
||||
|
||||
increment() {
|
||||
this.state.value++;
|
||||
}
|
||||
}
|
||||
|
||||
class App extends owl.Component {
|
||||
components = { Counter };
|
||||
}
|
||||
|
||||
const qweb = new owl.QWeb(TEMPLATES);
|
||||
const app = new App({ qweb });
|
||||
app.mount(document.body);
|
||||
```
|
||||
|
||||
Note that we assume here that the xml templates are available in the `TEMPLATES`
|
||||
string. More interesting examples can be found on the
|
||||
[playground](https://odoo.github.io/owl/playground) application.
|
||||
|
||||
## OWL's Design Principles
|
||||
|
||||
OWL is designed to be used in highly dynamic applications where changing
|
||||
@@ -37,42 +75,23 @@ one way to define components (with classes).
|
||||
If you are interested in a comparison with React or Vue, you will
|
||||
find some more information [here](doc/comparison.md).
|
||||
|
||||
## Example
|
||||
## Documentation
|
||||
|
||||
Here is a short example to illustrate interactive components:
|
||||
The complete documentation can be found [here](doc/readme.md). The most important sections are:
|
||||
|
||||
```xml
|
||||
<templates>
|
||||
<button t-name="Counter" t-on-click="increment">
|
||||
Click Me! [<t t-esc="state.value"/>]
|
||||
</button>
|
||||
</templates>
|
||||
```
|
||||
- [Quick Start](doc/quick_start.md)
|
||||
- [Component](doc/component.md)
|
||||
- [QWeb](doc/qweb.md)
|
||||
|
||||
```javascript
|
||||
class Counter extends owl.Component {
|
||||
state = { value: 0 };
|
||||
|
||||
increment() {
|
||||
this.state.value++;
|
||||
}
|
||||
}
|
||||
|
||||
const qweb = new owl.QWeb(TEMPLATES);
|
||||
const counter = new Counter({ qweb });
|
||||
counter.mount(document.body);
|
||||
```
|
||||
|
||||
Note that we assume here that the xml templates are available in the `TEMPLATES`
|
||||
string. More interesting examples can be found on the
|
||||
[playground](https://odoo.github.io/owl/playground) application.
|
||||
Found an issue in the documentation? A broken link? Some outdated information?
|
||||
Submit a PR!
|
||||
|
||||
## Installing/Building
|
||||
|
||||
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
||||
|
||||
- [owl-0.19.0.js](https://github.com/odoo/owl/releases/download/v0.19.0/owl.js)
|
||||
- [owl-0.19.0.min.js](https://github.com/odoo/owl/releases/download/v0.19.0/owl.min.js)
|
||||
- [owl-0.20.0.js](https://github.com/odoo/owl/releases/download/v0.20.0/owl.js)
|
||||
- [owl-0.20.0.min.js](https://github.com/odoo/owl/releases/download/v0.20.0/owl.min.js)
|
||||
|
||||
Some npm scripts are available:
|
||||
|
||||
@@ -86,17 +105,6 @@ Some npm scripts are available:
|
||||
| `npm run tools` | build tools applications, start a static server (see [here](doc/tooling.md)) |
|
||||
| `npm run tools:watch` | same as `tools`, but with a watcher to rebuild owl |
|
||||
|
||||
## Documentation
|
||||
|
||||
The complete documentation can be found [here](doc/readme.md). The most important sections are:
|
||||
|
||||
- [Quick Start](doc/quick_start.md)
|
||||
- [Component](doc/component.md)
|
||||
- [QWeb](doc/qweb.md)
|
||||
|
||||
Found an issue in the documentation? A broken link? Some outdated information?
|
||||
Submit a PR!
|
||||
|
||||
## License
|
||||
|
||||
OWL is [GPL licensed](./LICENSE).
|
||||
OWL is [LGPL licensed](./LICENSE).
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ const actions = {
|
||||
try {
|
||||
const loginInfo = await doSomeRPC("/login/", info);
|
||||
state.loginState = loginInfo;
|
||||
} catch {
|
||||
} catch (e) {
|
||||
state.loginState = "error";
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "owl",
|
||||
"version": "0.19.0",
|
||||
"version": "0.20.0",
|
||||
"description": "Odoo Web Library (OWL)",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
|
||||
+18
-18
@@ -238,23 +238,23 @@ QWeb.addDirective({
|
||||
if (key) {
|
||||
// we bind a variable to the key (could be a complex expression, so we
|
||||
// want to evaluate it only once)
|
||||
ctx.addLine(`let key${keyID} = ${key};`);
|
||||
ctx.addLine(`let key${keyID} = 'key' + ${key};`);
|
||||
}
|
||||
ctx.addLine(`let def${defID};`);
|
||||
let templateID = key
|
||||
? `key${keyID}`
|
||||
: ctx.inLoop
|
||||
? `String(-${componentID} - i)`
|
||||
? ctx.currentKey
|
||||
? `String(${ctx.currentKey} + '_k_' + i + '_c_' + ${componentID} )`
|
||||
: `String(-${componentID} - i)`
|
||||
: String(componentID);
|
||||
if (ctx.allowMultipleRoots) {
|
||||
// necessary to prevent collisions
|
||||
if (!key && ctx.inLoop) {
|
||||
let id = ctx.generateID();
|
||||
ctx.addLine(`let template${id} = "_slot_" + String(-${componentID} - i)`);
|
||||
templateID = `template${id}`;
|
||||
} else {
|
||||
templateID = `"_slot_${templateID}"`;
|
||||
}
|
||||
templateID = `"_slot_${templateID}"`;
|
||||
}
|
||||
if (key || ctx.inLoop) {
|
||||
let id = ctx.generateID();
|
||||
ctx.addLine(`let templateId${id} = ${templateID};`);
|
||||
templateID = `templateId${id}`;
|
||||
}
|
||||
|
||||
let ref = node.getAttribute("t-ref");
|
||||
@@ -422,7 +422,7 @@ QWeb.addDirective({
|
||||
const key = slotNode.getAttribute("t-set")!;
|
||||
slotNode.removeAttribute("t-set");
|
||||
const slotFn = qweb._compile(`slot_${key}_template`, slotNode, ctx);
|
||||
qweb.slots[`${slotId}_${key}`] = slotFn.bind(qweb);
|
||||
qweb.slots[`${slotId}_${key}`] = slotFn;
|
||||
}
|
||||
}
|
||||
if (clone.childNodes.length) {
|
||||
@@ -431,7 +431,7 @@ QWeb.addDirective({
|
||||
t.appendChild(child);
|
||||
}
|
||||
const slotFn = qweb._compile(`slot_default_template`, t, ctx);
|
||||
qweb.slots[`${slotId}_default`] = slotFn.bind(qweb);
|
||||
qweb.slots[`${slotId}_default`] = slotFn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,18 +449,18 @@ QWeb.addDirective({
|
||||
registerCode = `utils.defineProxy(vn${ctx.rootNode}, pvnode);`;
|
||||
}
|
||||
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}}}});${registerCode}w${componentID}.__owl__.pvnode = pvnode;});`
|
||||
`def${defID} = def${defID}.then(vnode=>{if (w${componentID}.__owl__.isDestroyed){return}${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.addElse();
|
||||
// need to update component
|
||||
let patchQueueCode = async ? `patchQueue${componentID}` : "extra.patchQueue";
|
||||
if (keepAlive) {
|
||||
// if we have t-keepalive="1", the component could be unmounted, but then
|
||||
// we __updateProps is called. This is ok, but we do not want to call
|
||||
// the willPatch/patched hooks of the component in this case, so we
|
||||
// disable the patch queue
|
||||
patchQueueCode = `w${componentID}.__owl__.isMounted ? ${patchQueueCode} : []`;
|
||||
// if we have t-keepalive="1", the component could be unmounted, but then
|
||||
// we __updateProps is called. This is ok, but we do not want to call
|
||||
// the willPatch/patched hooks of the component in this case, so we
|
||||
// disable the patch queue
|
||||
patchQueueCode = `w${componentID}.__owl__.isMounted ? ${patchQueueCode} : []`;
|
||||
}
|
||||
if (QWeb.dev) {
|
||||
ctx.addLine(`utils.validateProps(w${componentID}.constructor, props${componentID})`);
|
||||
|
||||
@@ -25,7 +25,6 @@ export class Observer {
|
||||
|
||||
notifyCB() {}
|
||||
async notifyChange() {
|
||||
|
||||
this.dirty = true;
|
||||
await Promise.resolve();
|
||||
if (this.dirty) {
|
||||
|
||||
@@ -82,6 +82,7 @@ QWeb.addDirective({
|
||||
if (node.nodeName !== "t") {
|
||||
let nodeID = qweb._compileGenericNode(node, ctx);
|
||||
ctx = ctx.withParent(nodeID);
|
||||
ctx = ctx.subContext("currentKey", ctx.lastNodeKey);
|
||||
}
|
||||
let value = ctx.getValue(node.getAttribute("t-esc")!);
|
||||
compileValueNode(value, node, qweb, ctx.subContext("escaping", true));
|
||||
@@ -96,6 +97,7 @@ QWeb.addDirective({
|
||||
if (node.nodeName !== "t") {
|
||||
let nodeID = qweb._compileGenericNode(node, ctx);
|
||||
ctx = ctx.withParent(nodeID);
|
||||
ctx = ctx.subContext("currentKey", ctx.lastNodeKey);
|
||||
}
|
||||
let value = ctx.getValue(node.getAttribute("t-raw")!);
|
||||
compileValueNode(value, node, qweb, ctx);
|
||||
@@ -202,8 +204,50 @@ QWeb.addDirective({
|
||||
const vars = Object.assign({}, ctx.variables, tempCtx.variables);
|
||||
ctx.rootContext.nextID = tempCtx.nextID;
|
||||
|
||||
const templateMap = Object.create(ctx.templates);
|
||||
// open new scope, if necessary
|
||||
const hasNewVariables = Object.keys(tempCtx.variables).length > 0;
|
||||
|
||||
// compile sub template
|
||||
let subCtx = ctx.subContext("caller", nodeCopy).subContext("variables", Object.create(vars));
|
||||
subCtx = subCtx.subContext("templates", templateMap);
|
||||
|
||||
if (templateMap[subTemplate]) {
|
||||
// OUCH, IT IS A RECURSIVE TEMPLATE SITUATION...
|
||||
// This is a tricky situation... We obviously cannot inline the compiled
|
||||
// template. So, what we need to do is to compile it, and make sure we
|
||||
// properly transfer everything from the current scope to the sub template.
|
||||
ctx.rootContext.shouldTrackScope = true;
|
||||
ctx.rootContext.shouldDefineOwner = true;
|
||||
let subTemplateName;
|
||||
if (ctx.hasParentWidget) {
|
||||
subTemplateName = ctx.templateName;
|
||||
} else {
|
||||
subTemplateName = `__${ctx.generateID()}`;
|
||||
subCtx.variables = {};
|
||||
let id = 0;
|
||||
for (let v in vars) {
|
||||
subCtx.variables[v] = vars[v];
|
||||
(vars[v] as any).id = `_v${id++}`;
|
||||
}
|
||||
const subTemplateFn = qweb._compile(subTemplateName, nodeTemplate.elem, subCtx);
|
||||
qweb.recursiveFns[subTemplateName] = subTemplateFn;
|
||||
}
|
||||
let varCode = `{}`;
|
||||
if (Object.keys(vars).length) {
|
||||
let id = 0;
|
||||
const content = Object.values(vars)
|
||||
.map((v: any) => `_v${id++}: ${v.expr}`)
|
||||
.join(",");
|
||||
varCode = `{${content}}`;
|
||||
}
|
||||
ctx.addLine(
|
||||
`this.recursiveFns['${subTemplateName}'].call(this, context, Object.assign({}, extra, {parentNode: c${ctx.parentNode}, vars: ${varCode}, scope}));`
|
||||
);
|
||||
return true;
|
||||
}
|
||||
templateMap[subTemplate] = true;
|
||||
|
||||
if (hasNewVariables) {
|
||||
ctx.addLine("{");
|
||||
ctx.indent();
|
||||
@@ -216,10 +260,6 @@ QWeb.addDirective({
|
||||
// todo: handle XML variables...
|
||||
}
|
||||
}
|
||||
|
||||
// compile sub template
|
||||
const subCtx = ctx.subContext("caller", nodeCopy).subContext("variables", Object.create(vars));
|
||||
|
||||
qweb._compileNode(nodeTemplate.elem, subCtx);
|
||||
|
||||
// close new scope
|
||||
@@ -283,9 +323,7 @@ QWeb.addDirective({
|
||||
}
|
||||
if (shouldWarn) {
|
||||
console.warn(
|
||||
`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}')`
|
||||
);
|
||||
}
|
||||
nodeCopy.removeAttribute("t-foreach");
|
||||
|
||||
@@ -28,10 +28,14 @@ export class Context {
|
||||
allowMultipleRoots: boolean = false;
|
||||
hasParentWidget: boolean = false;
|
||||
scopeVars: any[] = [];
|
||||
currentKey: string = "";
|
||||
lastNodeKey: string = ""; // temp variable to communicate to previous caller
|
||||
templates: { [key: string]: boolean } = {};
|
||||
|
||||
constructor(name?: string) {
|
||||
this.rootContext = this;
|
||||
this.templateName = name || "noname";
|
||||
this.templates[this.templateName] = true;
|
||||
this.addLine("var h = this.h;");
|
||||
}
|
||||
|
||||
|
||||
+12
-12
@@ -230,9 +230,7 @@ QWeb.addDirective({
|
||||
ctx.addLine(`const slot${slotKey} = this.slots[context.__owl__.slotId + '_' + '${value}'];`);
|
||||
ctx.addIf(`slot${slotKey}`);
|
||||
ctx.addLine(
|
||||
`slot${slotKey}(context.__owl__.parent, Object.assign({}, extra, {parentNode: c${
|
||||
ctx.parentNode
|
||||
}, vars: extra.vars, parent: owner}));`
|
||||
`slot${slotKey}.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c${ctx.parentNode}, vars: extra.vars, parent: owner}));`
|
||||
);
|
||||
ctx.closeIf();
|
||||
return true;
|
||||
@@ -250,31 +248,33 @@ QWeb.utils.toNumber = function(val: string): number | string {
|
||||
QWeb.addDirective({
|
||||
name: "model",
|
||||
priority: 42,
|
||||
atNodeCreation({ ctx, nodeID, value, node, fullName }) {
|
||||
atNodeCreation({ ctx, nodeID, value, node, fullName, addNodeHook }) {
|
||||
const type = node.getAttribute("type");
|
||||
let handler;
|
||||
let event = fullName.includes(".lazy") ? "change" : "input";
|
||||
const expr = ctx.formatExpression(`state.${value}`);
|
||||
if (node.tagName === "select") {
|
||||
ctx.addLine(`p${nodeID}.props = {value: context.state['${value}']};`);
|
||||
ctx.addLine(`p${nodeID}.props = {value: ${expr}};`);
|
||||
addNodeHook("create", `n.elm.value=${expr};`);
|
||||
event = "change";
|
||||
handler = `(ev) => {context.state['${value}'] = ev.target.value}`;
|
||||
handler = `(ev) => {${expr} = ev.target.value}`;
|
||||
} else if (type === "checkbox") {
|
||||
ctx.addLine(`p${nodeID}.props = {checked: context.state['${value}']};`);
|
||||
handler = `(ev) => {context.state['${value}'] = ev.target.checked}`;
|
||||
ctx.addLine(`p${nodeID}.props = {checked: ${expr}};`);
|
||||
handler = `(ev) => {${expr} = ev.target.checked}`;
|
||||
} else if (type === "radio") {
|
||||
const nodeValue = node.getAttribute("value")!;
|
||||
ctx.addLine(`p${nodeID}.props = {checked:context.state['${value}'] === '${nodeValue}'};`);
|
||||
handler = `(ev) => {context.state['${value}'] = ev.target.value}`;
|
||||
ctx.addLine(`p${nodeID}.props = {checked:${expr} === '${nodeValue}'};`);
|
||||
handler = `(ev) => {${expr} = ev.target.value}`;
|
||||
event = "click";
|
||||
} else {
|
||||
ctx.addLine(`p${nodeID}.props = {value: context.state['${value}']};`);
|
||||
ctx.addLine(`p${nodeID}.props = {value: ${expr}};`);
|
||||
const trimCode = fullName.includes(".trim") ? ".trim()" : "";
|
||||
let valueCode = `ev.target.value${trimCode}`;
|
||||
if (fullName.includes(".number")) {
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
valueCode = `utils.toNumber(${valueCode})`;
|
||||
}
|
||||
handler = `(ev) => {context.state['${value}'] = ${valueCode}}`;
|
||||
handler = `(ev) => {${expr} = ${valueCode}}`;
|
||||
}
|
||||
ctx.addLine(
|
||||
`extra.handlers['${event}' + ${nodeID}] = extra.handlers['${event}' + ${nodeID}] || (${handler});`
|
||||
|
||||
+13
-10
@@ -1,6 +1,7 @@
|
||||
import { EventBus } from "../core/event_bus";
|
||||
import { h, patch, VNode } from "../vdom/index";
|
||||
import { Context } from "./context";
|
||||
import { shallowEqual } from "../utils";
|
||||
|
||||
/**
|
||||
* Owl QWeb Engine
|
||||
@@ -92,14 +93,7 @@ const UTILS: Utils = {
|
||||
}
|
||||
return expr;
|
||||
},
|
||||
shallowEqual(p1, p2) {
|
||||
for (let k in p1) {
|
||||
if (p1[k] !== p2[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
shallowEqual
|
||||
};
|
||||
|
||||
function parseXML(xml: string): Document {
|
||||
@@ -163,6 +157,11 @@ export class QWeb extends EventBus {
|
||||
slots = {};
|
||||
nextSlotId = 1;
|
||||
|
||||
// recursiveTemplates contains sub templates called with t-call, but which
|
||||
// ends up in recursive situations. This is very similar to the slot situation,
|
||||
// as in we need to propagate the scope.
|
||||
recursiveFns = {};
|
||||
|
||||
isUpdating: boolean = false;
|
||||
|
||||
constructor(data?: string) {
|
||||
@@ -212,7 +211,7 @@ export class QWeb extends EventBus {
|
||||
* template, with the name given by the t-name attribute.
|
||||
*/
|
||||
addTemplates(xmlstr: string | Document) {
|
||||
const doc = typeof xmlstr === 'string' ? parseXML(xmlstr) : xmlstr;
|
||||
const doc = typeof xmlstr === "string" ? parseXML(xmlstr) : xmlstr;
|
||||
const templates = doc.getElementsByTagName("templates")[0];
|
||||
if (!templates) {
|
||||
return;
|
||||
@@ -328,6 +327,7 @@ export class QWeb extends EventBus {
|
||||
const isDebug = elem.attributes.hasOwnProperty("t-debug");
|
||||
const ctx = new Context(name);
|
||||
if (parentContext) {
|
||||
ctx.templates = Object.create(parentContext.templates);
|
||||
ctx.variables = Object.create(parentContext.variables);
|
||||
ctx.nextID = parentContext.parentNode! + 1;
|
||||
ctx.parentNode = parentContext.parentNode!;
|
||||
@@ -479,6 +479,7 @@ export class QWeb extends EventBus {
|
||||
if (node.nodeName !== "t") {
|
||||
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
|
||||
ctx = ctx.withParent(nodeID);
|
||||
ctx = ctx.subContext("currentKey", ctx.lastNodeKey);
|
||||
let nodeHooks = {};
|
||||
let addNodeHook = function(hook, handler) {
|
||||
nodeHooks[hook] = nodeHooks[hook] || [];
|
||||
@@ -647,7 +648,9 @@ export class QWeb extends EventBus {
|
||||
let nodeID = ctx.generateID();
|
||||
let nodeKey: any = (<Element>node).getAttribute("t-key");
|
||||
if (nodeKey) {
|
||||
nodeKey = ctx.formatExpression(nodeKey);
|
||||
ctx.addLine(`const nodeKey${nodeID} = ${ctx.formatExpression(nodeKey)}`);
|
||||
nodeKey = `nodeKey${nodeID}`;
|
||||
ctx.lastNodeKey = nodeKey;
|
||||
} else {
|
||||
nodeKey = nodeID;
|
||||
}
|
||||
|
||||
+27
-8
@@ -2,6 +2,7 @@ import { Env } from "../component/component";
|
||||
import { QWeb } from "../qweb/index";
|
||||
import { makeDirective } from "./directive";
|
||||
import { LINK_TEMPLATE, LINK_TEMPLATE_NAME } from "./Link";
|
||||
import { shallowEqual } from "../utils";
|
||||
|
||||
type NavigationGuard = (info: {
|
||||
env: Env;
|
||||
@@ -84,9 +85,6 @@ export class Router {
|
||||
this.routeIds.push(partialRoute.name);
|
||||
}
|
||||
|
||||
(this as any)._listener = () => this.matchAndApplyRules(this.currentPath());
|
||||
window.addEventListener("popstate", (this as any)._listener);
|
||||
|
||||
// setup link and directive
|
||||
env.qweb.addTemplate(LINK_TEMPLATE_NAME, LINK_TEMPLATE);
|
||||
QWeb.addDirective(makeDirective(<RouterEnv>env));
|
||||
@@ -97,6 +95,11 @@ export class Router {
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
async start() {
|
||||
(this as any)._listener = ev => this._navigate(this.currentPath(), ev);
|
||||
window.addEventListener("popstate", (this as any)._listener);
|
||||
if (this.mode === "hash") {
|
||||
window.addEventListener("hashchange", (this as any)._listener);
|
||||
}
|
||||
const result = await this.matchAndApplyRules(this.currentPath());
|
||||
if (result.type === "match") {
|
||||
this.currentRoute = result.route;
|
||||
@@ -110,18 +113,27 @@ export class Router {
|
||||
|
||||
async navigate(to: Destination): Promise<boolean> {
|
||||
const path = this.destToPath(to);
|
||||
return this._navigate(path);
|
||||
}
|
||||
async _navigate(path: string, ev?: any): Promise<boolean> {
|
||||
const initialName = this.currentRouteName;
|
||||
const initialParams = this.currentParams;
|
||||
const result = await this.matchAndApplyRules(path);
|
||||
if (result.type === "match") {
|
||||
const finalPath = this.routeToPath(result.route, result.params);
|
||||
this.setUrlFromPath(finalPath);
|
||||
const isPopStateEvent = ev && ev instanceof PopStateEvent;
|
||||
if (!isPopStateEvent) {
|
||||
this.setUrlFromPath(finalPath);
|
||||
}
|
||||
this.currentRoute = result.route;
|
||||
this.currentParams = result.params;
|
||||
} else if (result.type === "nomatch") {
|
||||
this.currentRoute = null;
|
||||
this.currentParams = null;
|
||||
}
|
||||
if (this.currentRouteName !== initialName) {
|
||||
const didChange =
|
||||
this.currentRouteName !== initialName || !shallowEqual(this.currentParams, initialParams);
|
||||
if (didChange) {
|
||||
this.env.qweb.forceUpdate();
|
||||
return true;
|
||||
}
|
||||
@@ -142,8 +154,11 @@ export class Router {
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private setUrlFromPath(path: string) {
|
||||
const url = location.origin + path;
|
||||
window.history.pushState({}, path, url);
|
||||
const separator = this.mode === "hash" ? "/" : "";
|
||||
const url = location.origin + separator + path;
|
||||
if (url !== window.location.href) {
|
||||
window.history.pushState({}, path, url);
|
||||
}
|
||||
}
|
||||
|
||||
private validateDestination(dest: Destination) {
|
||||
@@ -164,7 +179,8 @@ export class Router {
|
||||
parts[i] = <string>params[key];
|
||||
}
|
||||
}
|
||||
return parts.join("/");
|
||||
const prefix = this.mode === "hash" ? "#" : "";
|
||||
return prefix + parts.join("/");
|
||||
}
|
||||
|
||||
private currentPath(): string {
|
||||
@@ -223,6 +239,9 @@ export class Router {
|
||||
if (route.path === "*") {
|
||||
return {};
|
||||
}
|
||||
if (path.startsWith("#")) {
|
||||
path = path.slice(1);
|
||||
}
|
||||
const descrParts = route.path.split("/");
|
||||
const targetParts = path.split("/");
|
||||
const l = descrParts.length;
|
||||
|
||||
@@ -13,7 +13,10 @@ export function makeDirective(env: RouterEnv) {
|
||||
// make new t t-component element
|
||||
const comp = node.ownerDocument.createElement("t");
|
||||
comp.setAttribute("t-component", "__component__" + route.name);
|
||||
comp.setAttribute(first ? "t-if" : "t-elif", `env.router.currentRouteName === '${route.name}'`);
|
||||
comp.setAttribute(
|
||||
first ? "t-if" : "t-elif",
|
||||
`env.router.currentRouteName === '${route.name}'`
|
||||
);
|
||||
first = false;
|
||||
for (let param of route.params) {
|
||||
comp.setAttribute(param, `env.router.currentParams.${param}`);
|
||||
|
||||
@@ -56,6 +56,7 @@ export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S>
|
||||
const observer = store.observer;
|
||||
const revFn = this.deep ? observer.deepRevNumber : observer.revNumber;
|
||||
(this.__owl__ as any).store = store;
|
||||
(this.__owl__ as any).ownProps = this.props;
|
||||
(this.__owl__ as any).revFn = revFn.bind(observer);
|
||||
(this.__owl__ as any).storeHash = this.hashFunction(this.storeProps, {
|
||||
prevStoreProps: this.storeProps
|
||||
@@ -81,13 +82,44 @@ export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S>
|
||||
super.__destroy(parent);
|
||||
}
|
||||
|
||||
async render(force: boolean = false) {
|
||||
this.__updateStoreProps(this.props);
|
||||
|
||||
// this is quite technical, so this deserves some explanation.
|
||||
// When we have a connected component, it can be updated for 3 reasons:
|
||||
// - some internal state changes (this will go through this method)
|
||||
// - some props changes (if a parent is changed and need to rerender itself)
|
||||
// - a store update
|
||||
//
|
||||
// It is possible (with connected component and parent) to have the following
|
||||
// situation: the parent component is rendered first (from its state change),
|
||||
// then immediately after, it is rendered (from store update). Then, if the
|
||||
// __checkUpdate method is immediately over, the children component will
|
||||
// be rendered again by the store update, even though it is supposed to be
|
||||
// destroyed by the first rendering.
|
||||
//
|
||||
// So, the solution is to keep the information that there is a current
|
||||
// rendering occuring with the same store state, the same props, and return
|
||||
// that in the __checkUpdate method. To do this, we use the renderPromise
|
||||
// deferred, which is not used by the component system once the
|
||||
// component is ready, so we can use it for our own purpose.
|
||||
(this.__owl__ as any).renderPromise = super.render(force);
|
||||
return (this.__owl__ as any).renderPromise;
|
||||
}
|
||||
|
||||
async __updateProps(nextProps: P, f, p, s, v) {
|
||||
this.__updateStoreProps(nextProps);
|
||||
return super.__updateProps(nextProps, f, p, s, v);
|
||||
}
|
||||
|
||||
__updateStoreProps(nextProps): boolean {
|
||||
const store = (this.__owl__ as any).store;
|
||||
const __owl__ = this.__owl__ as any;
|
||||
const store = __owl__.store;
|
||||
const observer = store.observer;
|
||||
if (observer.rev === __owl__.rev && nextProps === __owl__.ownProps) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const storeProps = (<any>this.constructor).mapStoreToProps(
|
||||
store.state,
|
||||
nextProps,
|
||||
@@ -97,23 +129,21 @@ export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S>
|
||||
const storeHash = this.hashFunction(storeProps, options);
|
||||
this.storeProps = storeProps;
|
||||
let didChange = options.didChange;
|
||||
if (storeHash !== (this.__owl__ as any).storeHash) {
|
||||
(this.__owl__ as any).storeHash = storeHash;
|
||||
if (storeHash !== __owl__.storeHash) {
|
||||
__owl__.storeHash = storeHash;
|
||||
didChange = true;
|
||||
}
|
||||
(this.__owl__ as any).rev = store.observer.rev;
|
||||
__owl__.rev = store.observer.rev;
|
||||
__owl__.ownProps = nextProps;
|
||||
return didChange;
|
||||
}
|
||||
|
||||
async __checkUpdate() {
|
||||
const observer = (this.__owl__ as any).store.observer;
|
||||
if (observer.rev === (this.__owl__ as any).rev) {
|
||||
// update was already done by updateProps, from parent
|
||||
return;
|
||||
}
|
||||
const didChange = this.__updateStoreProps(this.props);
|
||||
if (didChange) {
|
||||
return this.render();
|
||||
}
|
||||
// see note in render method
|
||||
return (this.__owl__ as any).renderPromise;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -42,7 +42,7 @@ export class Store extends EventBus {
|
||||
debug: boolean;
|
||||
env: any;
|
||||
observer: Observer;
|
||||
getters: { [name: string]: (payload?) => any };
|
||||
getters: { [name: string]: (payload?) => any };
|
||||
|
||||
constructor(config: StoreConfig, options: StoreOption = {}) {
|
||||
super();
|
||||
@@ -54,13 +54,13 @@ export class Store extends EventBus {
|
||||
this.state = this.observer.observe(config.state || {});
|
||||
this.getters = {};
|
||||
if (config.getters) {
|
||||
const firstArg = {
|
||||
state: this.state,
|
||||
getters: this.getters,
|
||||
};
|
||||
for (let g in config.getters) {
|
||||
this.getters[g] = config.getters[g].bind(this, firstArg);
|
||||
}
|
||||
const firstArg = {
|
||||
state: this.state,
|
||||
getters: this.getters
|
||||
};
|
||||
for (let g in config.getters) {
|
||||
this.getters[g] = config.getters[g].bind(this, firstArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,3 +95,12 @@ export function debounce(func: Function, wait: number, immediate?: boolean): Fun
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function shallowEqual(p1, p2): boolean {
|
||||
for (let k in p1) {
|
||||
if (p1[k] !== p2[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -526,7 +526,7 @@ const htmlDomApi = {
|
||||
parentNode,
|
||||
nextSibling,
|
||||
tagName,
|
||||
setTextContent,
|
||||
setTextContent
|
||||
} as DOMAPI;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -31,7 +31,7 @@ exports[`animations t-transition combined with component 1`] = `
|
||||
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;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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();
|
||||
};
|
||||
utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||
@@ -76,7 +76,7 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
||||
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;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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();
|
||||
};
|
||||
utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||
|
||||
@@ -44,7 +44,7 @@ exports[`async rendering delayed component with t-asyncroot directive 1`] = `
|
||||
w8 = new W8(parent, props8);
|
||||
parent.__owl__.cmap[8] = w8.__owl__.id;
|
||||
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=>{if (w8.__owl__.isDestroyed){return}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 {
|
||||
def7 = def7 || w8.__updateProps(props8, extra.forceUpdate, extra.patchQueue);
|
||||
def7 = def7.then(()=>{if (w8.__owl__.isDestroyed) {return};let pvnode=w8.__owl__.pvnode;c5[_6_index]=pvnode;});
|
||||
@@ -72,7 +72,7 @@ exports[`async rendering delayed component with t-asyncroot directive 1`] = `
|
||||
w11 = new W11(parent, props11);
|
||||
parent.__owl__.cmap[11] = w11.__owl__.id;
|
||||
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=>{if (w11.__owl__.isDestroyed){return}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 {
|
||||
def10 = def10 || w11.__updateProps(props11, extra.forceUpdate, patchQueue11);
|
||||
def10 = def10.then(()=>{if (w11.__owl__.isDestroyed) {return};let pvnode=w11.__owl__.pvnode;c5[_9_index]=pvnode;});
|
||||
@@ -127,7 +127,7 @@ exports[`async rendering fast component with t-asyncroot directive 1`] = `
|
||||
w8 = new W8(parent, props8);
|
||||
parent.__owl__.cmap[8] = w8.__owl__.id;
|
||||
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=>{if (w8.__owl__.isDestroyed){return}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 {
|
||||
def7 = def7 || w8.__updateProps(props8, extra.forceUpdate, patchQueue8);
|
||||
def7 = def7.then(()=>{if (w8.__owl__.isDestroyed) {return};let pvnode=w8.__owl__.pvnode;c5[_6_index]=pvnode;});
|
||||
@@ -154,7 +154,7 @@ exports[`async rendering fast component with t-asyncroot directive 1`] = `
|
||||
w11 = new W11(parent, props11);
|
||||
parent.__owl__.cmap[11] = w11.__owl__.id;
|
||||
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=>{if (w11.__owl__.isDestroyed){return}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 {
|
||||
def10 = def10 || w11.__updateProps(props11, extra.forceUpdate, extra.patchQueue);
|
||||
def10 = def10.then(()=>{if (w11.__owl__.isDestroyed) {return};let pvnode=w11.__owl__.pvnode;c5[_9_index]=pvnode;});
|
||||
@@ -208,7 +208,7 @@ exports[`async rendering t-component with t-asyncroot directive: mixed re-render
|
||||
w8 = new W8(parent, props8);
|
||||
parent.__owl__.cmap[8] = w8.__owl__.id;
|
||||
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=>{if (w8.__owl__.isDestroyed){return}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 {
|
||||
def7 = def7 || w8.__updateProps(props8, extra.forceUpdate, extra.patchQueue);
|
||||
def7 = def7.then(()=>{if (w8.__owl__.isDestroyed) {return};let pvnode=w8.__owl__.pvnode;c5[_6_index]=pvnode;});
|
||||
@@ -236,7 +236,7 @@ exports[`async rendering t-component with t-asyncroot directive: mixed re-render
|
||||
w11 = new W11(parent, props11);
|
||||
parent.__owl__.cmap[11] = w11.__owl__.id;
|
||||
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=>{if (w11.__owl__.isDestroyed){return}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 {
|
||||
def10 = def10 || w11.__updateProps(props11, extra.forceUpdate, patchQueue11);
|
||||
def10 = def10.then(()=>{if (w11.__owl__.isDestroyed) {return};let pvnode=w11.__owl__.pvnode;c5[_9_index]=pvnode;});
|
||||
@@ -278,7 +278,7 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
def3 = def3 || w4.__updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};w4.el.style=_5;let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
|
||||
@@ -322,7 +322,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -380,7 +380,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -436,7 +436,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -479,7 +479,7 @@ exports[`composition sub components dom state with t-keepalive is preserved 1`]
|
||||
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.unmount();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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.unmount();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||
} else {
|
||||
def3 = def3 || w4.__updateProps(props4, extra.forceUpdate, w4.__owl__.isMounted ? extra.patchQueue : []);
|
||||
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w4.el,vn.elm);vn.elm=w4.el;w4.__remount();};c1[_2_index]=pvnode;});
|
||||
@@ -516,9 +516,10 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
|
||||
context.number = _3[i];
|
||||
context.number_value = _4[i];
|
||||
//COMPONENT
|
||||
let key8 = context['number'];
|
||||
let key8 = 'key' + context['number'];
|
||||
let def6;
|
||||
let w7 = key8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[key8]] : false;
|
||||
let templateId9 = key8;
|
||||
let w7 = templateId9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId9]] : false;
|
||||
let _5_index = c1.length;
|
||||
c1.push(null);
|
||||
let props7 = {};
|
||||
@@ -535,9 +536,9 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
|
||||
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[key8] = w7.__owl__.id;
|
||||
parent.__owl__.cmap[templateId9] = w7.__owl__.id;
|
||||
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=>{if (w7.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId9, 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 {
|
||||
def6 = def6 || w7.__updateProps(props7, extra.forceUpdate, extra.patchQueue);
|
||||
def6 = def6.then(()=>{if (w7.__owl__.isDestroyed) {return};let pvnode=w7.__owl__.pvnode;c1[_5_index]=pvnode;});
|
||||
@@ -579,7 +580,7 @@ exports[`composition t-component with dynamic value 1`] = `
|
||||
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;});
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -620,7 +621,7 @@ exports[`composition t-component with dynamic value 2 1`] = `
|
||||
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;});
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -662,7 +663,7 @@ exports[`lifecycle hooks willPatch/patched hook with t-keepalive 1`] = `
|
||||
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.unmount();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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.unmount();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||
} else {
|
||||
def3 = def3 || w4.__updateProps(props4, extra.forceUpdate, w4.__owl__.isMounted ? extra.patchQueue : []);
|
||||
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w4.el,vn.elm);vn.elm=w4.el;w4.__remount();};c1[_2_index]=pvnode;});
|
||||
@@ -704,7 +705,7 @@ exports[`other directives with t-component t-on with handler bound to argument 1
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -745,7 +746,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -786,7 +787,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -827,7 +828,7 @@ exports[`other directives with t-component t-on with handler bound to object 1`]
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -868,7 +869,7 @@ exports[`other directives with t-component t-on with prevent and self modifiers
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -909,7 +910,7 @@ exports[`other directives with t-component t-on with self and prevent modifiers
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -950,7 +951,7 @@ exports[`other directives with t-component t-on with self modifier 1`] = `
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -991,7 +992,7 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -1012,9 +1013,10 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
let key5 = 'somestring';
|
||||
let key5 = 'key' + 'somestring';
|
||||
let def3;
|
||||
let w4 = key5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[key5]] : false;
|
||||
let templateId6 = key5;
|
||||
let w4 = templateId6 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId6]] : false;
|
||||
let _2_index = c1.length;
|
||||
c1.push(null);
|
||||
let props4 = {flag:context['state'].flag};
|
||||
@@ -1031,9 +1033,9 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
|
||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[key5] = w4.__owl__.id;
|
||||
parent.__owl__.cmap[templateId6] = w4.__owl__.id;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId6, 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 {
|
||||
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;});
|
||||
@@ -1069,10 +1071,11 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
||||
context.item = _3[i];
|
||||
context.item_value = _4[i];
|
||||
//COMPONENT
|
||||
let key8 = context['item'];
|
||||
let key8 = 'key' + context['item'];
|
||||
let def6;
|
||||
let arg9 = context['item'];
|
||||
let w7 = key8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[key8]] : false;
|
||||
let templateId9 = key8;
|
||||
let arg10 = context['item'];
|
||||
let w7 = templateId9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId9]] : false;
|
||||
let _5_index = c1.length;
|
||||
c1.push(null);
|
||||
let props7 = {};
|
||||
@@ -1089,9 +1092,9 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
||||
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[key8] = w7.__owl__.id;
|
||||
parent.__owl__.cmap[templateId9] = w7.__owl__.id;
|
||||
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=>{if (w7.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, arg10));}};let pvnode=h(vnode.sel, {key: templateId9, 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 {
|
||||
def6 = def6 || w7.__updateProps(props7, extra.forceUpdate, extra.patchQueue);
|
||||
def6 = def6.then(()=>{if (w7.__owl__.isDestroyed) {return};let pvnode=w7.__owl__.pvnode;c1[_5_index]=pvnode;});
|
||||
@@ -1111,8 +1114,8 @@ exports[`t-model directive .lazy modifier 1`] = `
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
var vn2 = h('input', p2, c2);
|
||||
c1.push(vn2);
|
||||
p2.props = {value: context.state['text']};
|
||||
extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context.state['text'] = ev.target.value});
|
||||
p2.props = {value: context['state'].text};
|
||||
extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context['state'].text = ev.target.value});
|
||||
p2.on['change'] = extra.handlers['change' + 2];
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('span', p3, c3);
|
||||
@@ -1134,8 +1137,8 @@ exports[`t-model directive basic use, on an input 1`] = `
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
var vn2 = h('input', p2, c2);
|
||||
c1.push(vn2);
|
||||
p2.props = {value: context.state['text']};
|
||||
extra.handlers['input' + 2] = extra.handlers['input' + 2] || ((ev) => {context.state['text'] = ev.target.value});
|
||||
p2.props = {value: context['state'].text};
|
||||
extra.handlers['input' + 2] = extra.handlers['input' + 2] || ((ev) => {context['state'].text = ev.target.value});
|
||||
p2.on['input'] = extra.handlers['input' + 2];
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('span', p3, c3);
|
||||
@@ -1157,9 +1160,14 @@ exports[`t-model directive on a select 1`] = `
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
var vn2 = h('select', p2, c2);
|
||||
c1.push(vn2);
|
||||
p2.props = {value: context.state['color']};
|
||||
extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context.state['color'] = ev.target.value});
|
||||
p2.props = {value: context['state'].color};
|
||||
extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context['state'].color = ev.target.value});
|
||||
p2.on['change'] = extra.handlers['change' + 2];
|
||||
p2.hook = {
|
||||
create: (_, n) => {
|
||||
n.elm.value=context['state'].color;
|
||||
},
|
||||
};
|
||||
var _3 = '';
|
||||
let c4 = [], p4 = {key:4,attrs:{value: _3}};
|
||||
var vn4 = h('option', p4, c4);
|
||||
@@ -1187,6 +1195,29 @@ exports[`t-model directive on a select 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-model directive on a sub state key 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
var vn2 = h('input', p2, c2);
|
||||
c1.push(vn2);
|
||||
p2.props = {value: context['state'].something.text};
|
||||
extra.handlers['input' + 2] = extra.handlers['input' + 2] || ((ev) => {context['state'].something.text = ev.target.value});
|
||||
p2.on['input'] = extra.handlers['input' + 2];
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('span', p3, c3);
|
||||
c1.push(vn3);
|
||||
var _4 = context['state'].something.text;
|
||||
if (_4 || _4 === 0) {
|
||||
c3.push({text: _4});
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-model directive on an input type=radio 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
@@ -1199,8 +1230,8 @@ exports[`t-model directive on an input type=radio 1`] = `
|
||||
let c5 = [], p5 = {key:5,attrs:{type: _2,id: _3,value: _4},on:{}};
|
||||
var vn5 = h('input', p5, c5);
|
||||
c1.push(vn5);
|
||||
p5.props = {checked:context.state['choice'] === 'One'};
|
||||
extra.handlers['click' + 5] = extra.handlers['click' + 5] || ((ev) => {context.state['choice'] = ev.target.value});
|
||||
p5.props = {checked:context['state'].choice === 'One'};
|
||||
extra.handlers['click' + 5] = extra.handlers['click' + 5] || ((ev) => {context['state'].choice = ev.target.value});
|
||||
p5.on['click'] = extra.handlers['click' + 5];
|
||||
var _6 = 'radio';
|
||||
var _7 = 'two';
|
||||
@@ -1208,8 +1239,8 @@ exports[`t-model directive on an input type=radio 1`] = `
|
||||
let c9 = [], p9 = {key:9,attrs:{type: _6,id: _7,value: _8},on:{}};
|
||||
var vn9 = h('input', p9, c9);
|
||||
c1.push(vn9);
|
||||
p9.props = {checked:context.state['choice'] === 'Two'};
|
||||
extra.handlers['click' + 9] = extra.handlers['click' + 9] || ((ev) => {context.state['choice'] = ev.target.value});
|
||||
p9.props = {checked:context['state'].choice === 'Two'};
|
||||
extra.handlers['click' + 9] = extra.handlers['click' + 9] || ((ev) => {context['state'].choice = ev.target.value});
|
||||
p9.on['click'] = extra.handlers['click' + 9];
|
||||
let c10 = [], p10 = {key:10};
|
||||
var vn10 = h('span', p10, c10);
|
||||
@@ -1233,8 +1264,8 @@ exports[`t-model directive on an input, type=checkbox 1`] = `
|
||||
let c3 = [], p3 = {key:3,attrs:{type: _2},on:{}};
|
||||
var vn3 = h('input', p3, c3);
|
||||
c1.push(vn3);
|
||||
p3.props = {checked: context.state['flag']};
|
||||
extra.handlers['input' + 3] = extra.handlers['input' + 3] || ((ev) => {context.state['flag'] = ev.target.checked});
|
||||
p3.props = {checked: context['state'].flag};
|
||||
extra.handlers['input' + 3] = extra.handlers['input' + 3] || ((ev) => {context['state'].flag = ev.target.checked});
|
||||
p3.on['input'] = extra.handlers['input' + 3];
|
||||
let c4 = [], p4 = {key:4};
|
||||
var vn4 = h('span', p4, c4);
|
||||
@@ -1281,7 +1312,7 @@ exports[`t-slot directive can define and call slots 1`] = `
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
w4.__owl__.slotId = 1;
|
||||
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=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -1303,19 +1334,146 @@ exports[`t-slot directive can define and call slots 2`] = `
|
||||
c1.push(vn2);
|
||||
const slot3 = this.slots[context.__owl__.slotId + '_' + 'header'];
|
||||
if (slot3) {
|
||||
slot3(context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||
slot3.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
let c4 = [], p4 = {key:4};
|
||||
var vn4 = h('div', p4, c4);
|
||||
c1.push(vn4);
|
||||
const slot5 = this.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||
if (slot5) {
|
||||
slot5(context.__owl__.parent, Object.assign({}, extra, {parentNode: c4, vars: extra.vars, parent: owner}));
|
||||
slot5.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c4, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive can define and call slots 3`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('span', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`header\`});
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive can define and call slots 4`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('span', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`footer\`});
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive content is the default slot 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('span', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`sts rocks\`});
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive default slot work with text nodes 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
c1.push({text: \`sts rocks\`});
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive multiple roots are allowed in a default slot 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('span', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`sts\`});
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('span', p3, c3);
|
||||
c1.push(vn3);
|
||||
c3.push({text: \`rocks\`});
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive multiple roots are allowed in a named slot 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('span', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`sts\`});
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('span', p3, c3);
|
||||
c1.push(vn3);
|
||||
c3.push({text: \`rocks\`});
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive refs are properly bound in slots 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
var vn2 = h('button', p2, c2);
|
||||
c1.push(vn2);
|
||||
if (!context['doSomething']) {
|
||||
throw new Error('Missing handler \\\\'' + 'doSomething' + \`\\\\' when evaluating template 'slot_footer_template'\`)
|
||||
}
|
||||
extra.handlers['click' + 2] = extra.handlers['click' + 2] || context['doSomething'].bind(owner);
|
||||
p2.on['click'] = extra.handlers['click' + 2];
|
||||
const ref3 = \`myButton\`;
|
||||
p2.hook = {
|
||||
create: (_, n) => {
|
||||
context.refs[ref3] = n.elm;
|
||||
},
|
||||
};
|
||||
c2.push({text: \`do something\`});
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive slots are rendered with proper context 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
var vn2 = h('button', p2, c2);
|
||||
c1.push(vn2);
|
||||
if (!context['doSomething']) {
|
||||
throw new Error('Missing handler \\\\'' + 'doSomething' + \`\\\\' when evaluating template 'slot_footer_template'\`)
|
||||
}
|
||||
extra.handlers['click' + 2] = extra.handlers['click' + 2] || context['doSomething'].bind(owner);
|
||||
p2.on['click'] = extra.handlers['click' + 2];
|
||||
c2.push({text: \`do something\`});
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
@@ -1326,7 +1484,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 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}));
|
||||
slot3.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
return vn2;
|
||||
}"
|
||||
@@ -1366,12 +1524,14 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
scope.user = context.user;
|
||||
context.user_value = _5[i];
|
||||
scope.user_value = context.user_value;
|
||||
let c6 = [], p6 = {key:context['user'].id};
|
||||
const nodeKey6 = context['user'].id
|
||||
let c6 = [], p6 = {key:nodeKey6};
|
||||
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 templateId10 = String(nodeKey6 + '_k_' + i + '_c_' + 9 );
|
||||
let w9 = templateId10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId10]] : false;
|
||||
let _7_index = c6.length;
|
||||
c6.push(null);
|
||||
let props9 = {to:'/user/'+context['user'].id};
|
||||
@@ -1388,10 +1548,10 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
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;
|
||||
parent.__owl__.cmap[templateId10] = 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;});
|
||||
def8 = def8.then(vnode=>{if (w9.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId10, 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;});
|
||||
@@ -1402,6 +1562,20 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive slots are rendered with proper context, part 2 3`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c6 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
c6.push({text: \`User \`});
|
||||
var _7 = context['user'].name;
|
||||
if (_7 || _7 === 0) {
|
||||
c6.push({text: _7});
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
@@ -1412,7 +1586,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 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}));
|
||||
slot3.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
return vn2;
|
||||
}"
|
||||
@@ -1452,13 +1626,15 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
scope.user = context.user;
|
||||
context.user_value = _5[i];
|
||||
scope.user_value = context.user_value;
|
||||
let c6 = [], p6 = {key:context['user'].id};
|
||||
const nodeKey6 = context['user'].id
|
||||
let c6 = [], p6 = {key:nodeKey6};
|
||||
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 templateId11 = String(nodeKey6 + '_k_' + i + '_c_' + 10 );
|
||||
let w10 = templateId11 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId11]] : false;
|
||||
let _8_index = c6.length;
|
||||
c6.push(null);
|
||||
let props10 = {to:'/user/'+context['user'].id};
|
||||
@@ -1475,10 +1651,10 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
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;
|
||||
parent.__owl__.cmap[templateId11] = 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;});
|
||||
def9 = def9.then(vnode=>{if (w10.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId11, 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;});
|
||||
@@ -1489,6 +1665,19 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive slots are rendered with proper context, part 3 3`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c6 = extra.parentNode;
|
||||
let _7 = extra.vars._7
|
||||
Object.assign(context, extra.scope);
|
||||
if (_7 || _7 === 0) {
|
||||
c6.push({text: _7});
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
@@ -1522,7 +1711,7 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||
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;});
|
||||
def4 = def4.then(vnode=>{if (w5.__owl__.isDestroyed){return}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;});
|
||||
@@ -1532,6 +1721,19 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive slots are rendered with proper context, part 4 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
let _2 = extra.vars._2
|
||||
Object.assign(context, extra.scope);
|
||||
if (_2 || _2 === 0) {
|
||||
c1.push({text: _2});
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`top level sub widgets basic use 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
@@ -1562,7 +1764,7 @@ exports[`top level sub widgets basic use 1`] = `
|
||||
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;});
|
||||
def2 = def2.then(vnode=>{if (w3.__owl__.isDestroyed){return}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);});
|
||||
@@ -1603,7 +1805,7 @@ exports[`top level sub widgets can select a sub widget 1`] = `
|
||||
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;});
|
||||
def2 = def2.then(vnode=>{if (w3.__owl__.isDestroyed){return}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);});
|
||||
@@ -1632,7 +1834,7 @@ exports[`top level sub widgets can select a sub widget 1`] = `
|
||||
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;});
|
||||
def6 = def6.then(vnode=>{if (w7.__owl__.isDestroyed){return}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);});
|
||||
|
||||
@@ -36,7 +36,7 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
|
||||
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;});
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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);
|
||||
|
||||
@@ -160,6 +160,30 @@ describe("basic widget properties", () => {
|
||||
widget.render();
|
||||
expect(fixture.innerHTML).toBe(`<div><span></span></div>`);
|
||||
});
|
||||
|
||||
test("reconciliation alg is not confused in some specific situation", async () => {
|
||||
// in this test, we set t-key to 4 because it was in conflict with the
|
||||
// template id corresponding to the first child.
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Parent">
|
||||
<Child />
|
||||
<Child t-key="4"/>
|
||||
</div>
|
||||
<span t-name="Child">child</span>
|
||||
</templates>
|
||||
`);
|
||||
|
||||
class Child extends Component<any, any, any> {}
|
||||
|
||||
class Parent extends Component<any, any, any> {
|
||||
components = { Child };
|
||||
}
|
||||
|
||||
const widget = new Parent(env);
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>child</span><span>child</span></div>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("lifecycle hooks", () => {
|
||||
@@ -833,6 +857,50 @@ describe("destroy method", () => {
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
expect(isRendered).toBe(false);
|
||||
});
|
||||
|
||||
test("destroying a widget before being mounted", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Parent" t-on-some-event="doStuff">
|
||||
<Child />
|
||||
</div>
|
||||
<span t-name="Child">
|
||||
<GrandChild t-if="state.flag" val="something"/>
|
||||
<button t-on-click="doSomething">click</button>
|
||||
</span>
|
||||
<span t-name="GrandChild">
|
||||
<t t-esc="props.val.val"/>
|
||||
</span>
|
||||
</templates>`);
|
||||
class Parent extends Component<any, any, any> {
|
||||
components = { Child };
|
||||
state = { p: 1 };
|
||||
doStuff() {
|
||||
this.state.p = 2;
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends Component<any, any, any> {
|
||||
components = { GrandChild };
|
||||
state = { val: 33, flag: false };
|
||||
doSomething() {
|
||||
this.state.val = 12;
|
||||
this.state.flag = true;
|
||||
this.trigger("some-event");
|
||||
}
|
||||
get something() {
|
||||
return { val: this.state.val };
|
||||
}
|
||||
}
|
||||
class GrandChild extends Component<any, any, any> {}
|
||||
|
||||
const parent = new Parent(env);
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span><button>click</button></span></div>");
|
||||
fixture.querySelector("button")!.click();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span><span>12</span><button>click</button></span></div>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("composition", () => {
|
||||
@@ -1274,6 +1342,62 @@ describe("composition", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("list of sub components inside other nodes", async () => {
|
||||
// this confuses the patching algorithm...
|
||||
env.qweb.addTemplate("ChildWidget", `<span>child</span>`);
|
||||
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Parent">
|
||||
<div t-foreach="state.blips" t-as="blip" t-key="blip.id">
|
||||
<SubWidget />
|
||||
</div>
|
||||
</div>
|
||||
<span t-name="SubWidget">asdf</span>
|
||||
</templates>`);
|
||||
|
||||
class SubWidget extends Widget {}
|
||||
class Parent extends Widget {
|
||||
components = { SubWidget };
|
||||
state = { blips: [{ a: "a", id: 1 }, { b: "b", id: 2 }, { c: "c", id: 4 }] };
|
||||
}
|
||||
const parent = new Parent(env);
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><div><span>asdf</span></div><div><span>asdf</span></div><div><span>asdf</span></div></div>"
|
||||
);
|
||||
parent.state.blips.splice(0, 1);
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><div><span>asdf</span></div><div><span>asdf</span></div></div>"
|
||||
);
|
||||
});
|
||||
|
||||
test("list of two sub components inside other nodes", async () => {
|
||||
// this confuses the patching algorithm...
|
||||
env.qweb.addTemplate("ChildWidget", `<span>child</span>`);
|
||||
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Parent">
|
||||
<div t-foreach="state.blips" t-as="blip" t-key="blip.id">
|
||||
<SubWidget />
|
||||
<SubWidget />
|
||||
</div>
|
||||
</div>
|
||||
<span t-name="SubWidget">asdf</span>
|
||||
</templates>`);
|
||||
|
||||
class SubWidget extends Widget {}
|
||||
class Parent extends Widget {
|
||||
components = { SubWidget };
|
||||
state = { blips: [{ a: "a", id: 1 }] };
|
||||
}
|
||||
const parent = new Parent(env);
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>asdf</span><span>asdf</span></div></div>");
|
||||
});
|
||||
|
||||
test("t-component with dynamic value", async () => {
|
||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="{{state.widget}}"/></div>`);
|
||||
class ParentWidget extends Widget {
|
||||
@@ -2927,6 +3051,8 @@ describe("t-slot directive", () => {
|
||||
);
|
||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Dialog.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.slots['1_header'].toString()).toMatchSnapshot();
|
||||
expect(env.qweb.slots['1_footer'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slots are rendered with proper context", async () => {
|
||||
@@ -2962,6 +3088,7 @@ describe("t-slot directive", () => {
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
||||
);
|
||||
expect(env.qweb.slots['1_footer'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slots are rendered with proper context, part 2", async () => {
|
||||
@@ -2999,6 +3126,7 @@ describe("t-slot directive", () => {
|
||||
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>'
|
||||
);
|
||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slots are rendered with proper context, part 3", async () => {
|
||||
@@ -3037,6 +3165,7 @@ describe("t-slot directive", () => {
|
||||
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>'
|
||||
);
|
||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slots are rendered with proper context, part 4", async () => {
|
||||
@@ -3069,6 +3198,7 @@ describe("t-slot directive", () => {
|
||||
app.state.user.name = "David";
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User David</a></div>');
|
||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("refs are properly bound in slots", async () => {
|
||||
@@ -3104,6 +3234,7 @@ describe("t-slot directive", () => {
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
||||
);
|
||||
expect(env.qweb.slots['1_footer'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("content is the default slot", async () => {
|
||||
@@ -3125,6 +3256,7 @@ describe("t-slot directive", () => {
|
||||
await parent.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>sts rocks</span></div></div>");
|
||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("default slot work with text nodes", async () => {
|
||||
@@ -3144,6 +3276,7 @@ describe("t-slot directive", () => {
|
||||
await parent.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><div>sts rocks</div></div>");
|
||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("multiple roots are allowed in a named slot", async () => {
|
||||
@@ -3168,6 +3301,7 @@ describe("t-slot directive", () => {
|
||||
await parent.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
||||
expect(env.qweb.slots['1_content'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("multiple roots are allowed in a default slot", async () => {
|
||||
@@ -3190,6 +3324,7 @@ describe("t-slot directive", () => {
|
||||
await parent.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
||||
expect(env.qweb.slots['1_default'].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("missing slots are ignored", async () => {
|
||||
@@ -3459,6 +3594,50 @@ describe("t-model directive", () => {
|
||||
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("on a select, initial state", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<select t-model="color">
|
||||
<option value="">Please select one</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="blue">Blue</option>
|
||||
</select>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
state = { color: "red" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
const select = fixture.querySelector("select")!;
|
||||
expect(select.value).toBe("red");
|
||||
});
|
||||
|
||||
test("on a sub state key", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input t-model="something.text"/>
|
||||
<span><t t-esc="state.something.text"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
|
||||
class SomeComponent extends Widget {
|
||||
state = { something: {text: "" }};
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
await comp.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><input><span></span></div>");
|
||||
|
||||
const input = fixture.querySelector("input")!;
|
||||
await editInput(input, "test");
|
||||
expect(comp.state.something.text).toBe("test");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test(".lazy modifier", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
|
||||
@@ -280,7 +280,7 @@ describe("props validation", () => {
|
||||
|
||||
test("props: extra props cause an error, part 2", async () => {
|
||||
class TestWidget extends Widget {
|
||||
static props = {message: true};
|
||||
static props = { message: true };
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
@@ -294,23 +294,22 @@ describe("props validation", () => {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
QWeb.utils.validateProps(TestWidget, { message: 1});
|
||||
QWeb.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}};
|
||||
static props = { message: { type: String, optional: true } };
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
QWeb.utils.validateProps(TestWidget, { message: undefined});
|
||||
QWeb.utils.validateProps(TestWidget, { message: undefined });
|
||||
}).not.toThrow();
|
||||
expect(() => {
|
||||
QWeb.utils.validateProps(TestWidget, { message: null});
|
||||
QWeb.utils.validateProps(TestWidget, { message: null });
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("default props", () => {
|
||||
|
||||
@@ -65,7 +65,7 @@ describe("observer", () => {
|
||||
expect(observer.revNumber(obj)).toBe(1);
|
||||
expect(observer.deepRevNumber(obj)).toBe(1);
|
||||
expect(observer.rev).toBe(1);
|
||||
expect(typeof obj.date.getFullYear()).toBe('number');
|
||||
expect(typeof obj.date.getFullYear()).toBe("number");
|
||||
expect(obj.date).toBe(date);
|
||||
|
||||
obj.date = new Date();
|
||||
|
||||
@@ -361,7 +361,8 @@ exports[`foreach iterate on items (on a element node) 1`] = `
|
||||
context.item_index = i;
|
||||
context.item = _3[i];
|
||||
context.item_value = _4[i];
|
||||
let c5 = [], p5 = {key:context['item']};
|
||||
const nodeKey5 = context['item']
|
||||
let c5 = [], p5 = {key:nodeKey5};
|
||||
var vn5 = h('span', p5, c5);
|
||||
c1.push(vn5);
|
||||
var _6 = context['item'];
|
||||
@@ -788,6 +789,226 @@ exports[`t-call (template calling inherit context 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling recursive template, part 1 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('span', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`hey\`});
|
||||
if (false) {
|
||||
this.recursiveFns['__3'].call(this, context, Object.assign({}, extra, {parentNode: c1, vars: {}, scope}));
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling recursive template, part 1 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
Object.assign(context, extra.scope);
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('div', p2, c2);
|
||||
c1.push(vn2);
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('span', p3, c3);
|
||||
c2.push(vn3);
|
||||
c3.push({text: \`hey\`});
|
||||
if (false) {
|
||||
this.recursiveFns['__3'].call(this, context, Object.assign({}, extra, {parentNode: c2, vars: {}, scope}));
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling recursive template, part 2 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
context = Object.create(context);
|
||||
const scope = Object.create(null);
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
{
|
||||
let _2 = context['root'];
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('div', p3, c3);
|
||||
c1.push(vn3);
|
||||
let c4 = [], p4 = {key:4};
|
||||
var vn4 = h('p', p4, c4);
|
||||
c3.push(vn4);
|
||||
var _5 = _2.val;
|
||||
if (_5 || _5 === 0) {
|
||||
c4.push({text: _5});
|
||||
}
|
||||
var _6 = _2.children||[];
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
var _7 = _8 = _6;
|
||||
if (!(_6 instanceof Array)) {
|
||||
_7 = Object.keys(_6);
|
||||
_8 = Object.values(_6);
|
||||
}
|
||||
var _length7 = _7.length;
|
||||
for (let i = 0; i < _length7; i++) {
|
||||
context.subtree_first = i === 0;
|
||||
scope.subtree_first = context.subtree_first;
|
||||
context.subtree_last = i === _length7 - 1;
|
||||
scope.subtree_last = context.subtree_last;
|
||||
context.subtree_index = i;
|
||||
scope.subtree_index = context.subtree_index;
|
||||
context.subtree = _7[i];
|
||||
scope.subtree = context.subtree;
|
||||
context.subtree_value = _8[i];
|
||||
scope.subtree_value = context.subtree_value;
|
||||
this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c3, vars: {_v0: context['subtree']}, scope}));
|
||||
}
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling recursive template, part 2 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
context = Object.create(context);
|
||||
const scope = Object.create(null);
|
||||
var h = this.h;
|
||||
let c3 = extra.parentNode;
|
||||
let _v0 = extra.vars._v0
|
||||
Object.assign(context, extra.scope);
|
||||
let c4 = [], p4 = {key:4};
|
||||
var vn4 = h('div', p4, c4);
|
||||
c3.push(vn4);
|
||||
let c5 = [], p5 = {key:5};
|
||||
var vn5 = h('p', p5, c5);
|
||||
c4.push(vn5);
|
||||
var _6 = _v0.val;
|
||||
if (_6 || _6 === 0) {
|
||||
c5.push({text: _6});
|
||||
}
|
||||
var _7 = _v0.children||[];
|
||||
if (!_7) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
var _8 = _9 = _7;
|
||||
if (!(_7 instanceof Array)) {
|
||||
_8 = Object.keys(_7);
|
||||
_9 = Object.values(_7);
|
||||
}
|
||||
var _length8 = _8.length;
|
||||
for (let i = 0; i < _length8; i++) {
|
||||
context.subtree_first = i === 0;
|
||||
scope.subtree_first = context.subtree_first;
|
||||
context.subtree_last = i === _length8 - 1;
|
||||
scope.subtree_last = context.subtree_last;
|
||||
context.subtree_index = i;
|
||||
scope.subtree_index = context.subtree_index;
|
||||
context.subtree = _8[i];
|
||||
scope.subtree = context.subtree;
|
||||
context.subtree_value = _9[i];
|
||||
scope.subtree_value = context.subtree_value;
|
||||
this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c4, vars: {_v0: context['subtree']}, scope}));
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling recursive template, part 3 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
context = Object.create(context);
|
||||
const scope = Object.create(null);
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
{
|
||||
let _2 = context['root'];
|
||||
let c3 = [], p3 = {key:3};
|
||||
var vn3 = h('div', p3, c3);
|
||||
c1.push(vn3);
|
||||
let c4 = [], p4 = {key:4};
|
||||
var vn4 = h('p', p4, c4);
|
||||
c3.push(vn4);
|
||||
var _5 = _2.val;
|
||||
if (_5 || _5 === 0) {
|
||||
c4.push({text: _5});
|
||||
}
|
||||
var _6 = _2.children||[];
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
var _7 = _8 = _6;
|
||||
if (!(_6 instanceof Array)) {
|
||||
_7 = Object.keys(_6);
|
||||
_8 = Object.values(_6);
|
||||
}
|
||||
var _length7 = _7.length;
|
||||
for (let i = 0; i < _length7; i++) {
|
||||
context.subtree_first = i === 0;
|
||||
scope.subtree_first = context.subtree_first;
|
||||
context.subtree_last = i === _length7 - 1;
|
||||
scope.subtree_last = context.subtree_last;
|
||||
context.subtree_index = i;
|
||||
scope.subtree_index = context.subtree_index;
|
||||
context.subtree = _7[i];
|
||||
scope.subtree = context.subtree;
|
||||
context.subtree_value = _8[i];
|
||||
scope.subtree_value = context.subtree_value;
|
||||
this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c3, vars: {_v0: context['subtree']}, scope}));
|
||||
}
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling recursive template, part 3 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
context = Object.create(context);
|
||||
const scope = Object.create(null);
|
||||
var h = this.h;
|
||||
let c3 = extra.parentNode;
|
||||
let _v0 = extra.vars._v0
|
||||
Object.assign(context, extra.scope);
|
||||
let c4 = [], p4 = {key:4};
|
||||
var vn4 = h('div', p4, c4);
|
||||
c3.push(vn4);
|
||||
let c5 = [], p5 = {key:5};
|
||||
var vn5 = h('p', p5, c5);
|
||||
c4.push(vn5);
|
||||
var _6 = _v0.val;
|
||||
if (_6 || _6 === 0) {
|
||||
c5.push({text: _6});
|
||||
}
|
||||
var _7 = _v0.children||[];
|
||||
if (!_7) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
var _8 = _9 = _7;
|
||||
if (!(_7 instanceof Array)) {
|
||||
_8 = Object.keys(_7);
|
||||
_9 = Object.values(_7);
|
||||
}
|
||||
var _length8 = _8.length;
|
||||
for (let i = 0; i < _length8; i++) {
|
||||
context.subtree_first = i === 0;
|
||||
scope.subtree_first = context.subtree_first;
|
||||
context.subtree_last = i === _length8 - 1;
|
||||
scope.subtree_last = context.subtree_last;
|
||||
context.subtree_index = i;
|
||||
scope.subtree_index = context.subtree_index;
|
||||
context.subtree = _8[i];
|
||||
scope.subtree = context.subtree;
|
||||
context.subtree_value = _9[i];
|
||||
scope.subtree_value = context.subtree_value;
|
||||
this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c4, vars: {_v0: context['subtree']}, scope}));
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-call (template calling scoped parameters 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
@@ -1095,7 +1316,8 @@ exports[`t-key can use t-key directive on a node 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:context['beer'].id};
|
||||
const nodeKey1 = context['beer'].id
|
||||
let c1 = [], p1 = {key:nodeKey1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['beer'].name;
|
||||
if (_2 || _2 === 0) {
|
||||
@@ -1126,7 +1348,8 @@ exports[`t-key t-key directive in a list 1`] = `
|
||||
context.beer_index = i;
|
||||
context.beer = _3[i];
|
||||
context.beer_value = _4[i];
|
||||
let c5 = [], p5 = {key:context['beer'].id};
|
||||
const nodeKey5 = context['beer'].id
|
||||
let c5 = [], p5 = {key:nodeKey5};
|
||||
var vn5 = h('li', p5, c5);
|
||||
c1.push(vn5);
|
||||
var _6 = context['beer'].name;
|
||||
@@ -1227,7 +1450,8 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
|
||||
context.action_index = i;
|
||||
context.action = _3[i];
|
||||
context.action_value = _4[i];
|
||||
let c5 = [], p5 = {key:context['action_index']};
|
||||
const nodeKey5 = context['action_index']
|
||||
let c5 = [], p5 = {key:nodeKey5};
|
||||
var vn5 = h('li', p5, c5);
|
||||
c1.push(vn5);
|
||||
let c6 = [], p6 = {key:6,on:{}};
|
||||
@@ -1560,7 +1784,8 @@ exports[`t-ref refs in a loop 1`] = `
|
||||
context.item_index = i;
|
||||
context.item = _3[i];
|
||||
context.item_value = _4[i];
|
||||
let c5 = [], p5 = {key:context['item']};
|
||||
const nodeKey5 = context['item']
|
||||
let c5 = [], p5 = {key:nodeKey5};
|
||||
var vn5 = h('div', p5, c5);
|
||||
c1.push(vn5);
|
||||
const ref6 = (context['item']);
|
||||
@@ -1720,7 +1945,8 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
||||
context.elem_index = i;
|
||||
context.elem = _4[i];
|
||||
context.elem_value = _5[i];
|
||||
let c6 = [], p6 = {key:context['elem_index']};
|
||||
const nodeKey6 = context['elem_index']
|
||||
let c6 = [], p6 = {key:nodeKey6};
|
||||
var vn6 = h('div', p6, c6);
|
||||
c1.push(vn6);
|
||||
let c7 = [], p7 = {key:7};
|
||||
|
||||
@@ -573,6 +573,76 @@ describe("t-call (template calling", () => {
|
||||
const expected = "<div>ok</div>";
|
||||
expect(trim(renderToString(qweb, "caller"))).toBe(expected);
|
||||
});
|
||||
|
||||
test("recursive template, part 1", () => {
|
||||
qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="recursive">
|
||||
<span>hey</span>
|
||||
<t t-if="false">
|
||||
<t t-call="recursive"/>
|
||||
</t>
|
||||
</div>
|
||||
</templates>
|
||||
`);
|
||||
const expected = "<div><span>hey</span></div>";
|
||||
expect(renderToString(qweb, "recursive")).toBe(expected);
|
||||
const recursiveFn = Object.values(qweb.recursiveFns)[0];
|
||||
expect(recursiveFn.toString()).toMatchSnapshot();
|
||||
|
||||
});
|
||||
|
||||
test("recursive template, part 2", () => {
|
||||
qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Parent">
|
||||
<t t-call="nodeTemplate">
|
||||
<t t-set="node" t-value="root"/>
|
||||
</t>
|
||||
</div>
|
||||
<div t-name="nodeTemplate">
|
||||
<p><t t-esc="node.val"/></p>
|
||||
<t t-foreach="node.children or []" t-as="subtree">
|
||||
<t t-call="nodeTemplate">
|
||||
<t t-set="node" t-value="subtree"/>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
</templates>
|
||||
`);
|
||||
const root = { val: "a", children: [{val: "b"}, {val: "c"}]};
|
||||
const expected = "<div><div><p>a</p><div><p>b</p></div><div><p>c</p></div></div></div>";
|
||||
expect(renderToString(qweb, "Parent", {root })).toBe(expected);
|
||||
const recursiveFn = Object.values(qweb.recursiveFns)[0];
|
||||
expect(recursiveFn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("recursive template, part 3", () => {
|
||||
qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Parent">
|
||||
<t t-call="nodeTemplate">
|
||||
<t t-set="node" t-value="root"/>
|
||||
</t>
|
||||
</div>
|
||||
<div t-name="nodeTemplate">
|
||||
<p><t t-esc="node.val"/></p>
|
||||
<t t-foreach="node.children or []" t-as="subtree">
|
||||
<t t-call="nodeTemplate">
|
||||
<t t-set="node" t-value="subtree"/>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
</templates>
|
||||
`);
|
||||
const root = { val: "a", children: [{val: "b", children: [{val: "d"}]}, {val: "c"}]};
|
||||
const expected = "<div><div><p>a</p><div><p>b</p><div><p>d</p></div></div><div><p>c</p></div></div></div>";
|
||||
expect(renderToString(qweb, "Parent", {root })).toBe(expected);
|
||||
const recursiveFn = Object.values(qweb.recursiveFns)[0];
|
||||
expect(recursiveFn.toString()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("foreach", () => {
|
||||
|
||||
@@ -17,7 +17,7 @@ exports[`Link component can render simple cases 1`] = `
|
||||
p3.on['click'] = extra.handlers['click' + 3];
|
||||
const slot4 = this.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot4) {
|
||||
slot4(context.__owl__.parent, Object.assign({}, extra, {parentNode: c3, vars: extra.vars, parent: owner}));
|
||||
slot4.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c3, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
return vn3;
|
||||
}"
|
||||
|
||||
@@ -32,7 +32,7 @@ exports[`router directive t-routecomponent can render parameterized route 1`] =
|
||||
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;});
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -75,7 +75,7 @@ exports[`router directive t-routecomponent can render parameterized route with s
|
||||
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;});
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -118,7 +118,7 @@ exports[`router directive t-routecomponent can render simple cases 1`] = `
|
||||
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;});
|
||||
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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 {
|
||||
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;});
|
||||
@@ -147,7 +147,7 @@ exports[`router directive t-routecomponent can render simple cases 1`] = `
|
||||
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();}}});c1[_5_index]=pvnode;w7.__owl__.pvnode = pvnode;});
|
||||
def6 = def6.then(vnode=>{if (w7.__owl__.isDestroyed){return}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();}}});c1[_5_index]=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;c1[_5_index]=pvnode;});
|
||||
|
||||
+103
-25
@@ -1,5 +1,5 @@
|
||||
import { Destination, Router, RouterEnv, Route } from "../../src/router/Router";
|
||||
import { makeTestEnv } from "../helpers";
|
||||
import { Destination, RouterEnv, Route } from "../../src/router/Router";
|
||||
import { makeTestEnv, nextTick } from "../helpers";
|
||||
import { TestRouter } from "./TestRouter";
|
||||
|
||||
let env: RouterEnv;
|
||||
@@ -25,18 +25,58 @@ describe("router miscellaneous", () => {
|
||||
]);
|
||||
}).toThrow(`Invalid destination: {"abc":"hey"}`);
|
||||
});
|
||||
|
||||
test("navigate to same route but with different params should trigger update", async () => {
|
||||
router = new TestRouter(env, [{ name: "users", path: "/users/{{id}}" }]);
|
||||
env.qweb.forceUpdate = jest.fn();
|
||||
await router.navigate({ to: "users", params: { id: 3 } });
|
||||
expect(window.location.pathname).toBe("/users/3");
|
||||
expect(env.qweb.forceUpdate).toHaveBeenCalledTimes(1);
|
||||
|
||||
await router.navigate({ to: "users", params: { id: 5 } });
|
||||
expect(window.location.pathname).toBe("/users/5");
|
||||
expect(env.qweb.forceUpdate).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test("changing url to same route but with different params should trigger update (hash mode)", async () => {
|
||||
env.qweb.forceUpdate = jest.fn();
|
||||
router = new TestRouter(env, [{ name: "users", path: "/users/{{id}}" }], { mode: "hash" });
|
||||
await router.start();
|
||||
await router.navigate({ to: "users", params: { id: 3 } });
|
||||
expect(window.location.hash).toBe("#/users/3");
|
||||
expect(env.qweb.forceUpdate).toHaveBeenCalledTimes(1);
|
||||
|
||||
window.location.hash = "/users/5";
|
||||
window.dispatchEvent(new Event("hashchange"));
|
||||
await nextTick();
|
||||
expect(window.location.hash).toBe("#/users/5");
|
||||
expect(env.qweb.forceUpdate).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("routeToPath", () => {
|
||||
const routeToPath = Router.prototype["routeToPath"];
|
||||
test("simple non parameterized path", () => {
|
||||
expect(routeToPath({path: "/abc"} as Route, {})).toBe("/abc");
|
||||
expect(routeToPath({path: "/abc/def"} as Route, {})).toBe("/abc/def");
|
||||
expect(routeToPath({path: "/abc"} as Route, { val: 12 })).toBe("/abc");
|
||||
router = new TestRouter(env, []);
|
||||
expect(router["routeToPath"]({ path: "/abc" } as Route, {})).toBe("/abc");
|
||||
expect(router["routeToPath"]({ path: "/abc/def" } as Route, {})).toBe("/abc/def");
|
||||
expect(router["routeToPath"]({ path: "/abc" } as Route, { val: 12 })).toBe("/abc");
|
||||
});
|
||||
|
||||
test("simple parameterized path", () => {
|
||||
expect(routeToPath({path: "/abc/{{def}}"} as Route, { def: 34 })).toBe("/abc/34");
|
||||
router = new TestRouter(env, []);
|
||||
expect(router["routeToPath"]({ path: "/abc/{{def}}" } as Route, { def: 34 })).toBe("/abc/34");
|
||||
});
|
||||
|
||||
test("simple non parameterized path, mode = hash", () => {
|
||||
router = new TestRouter(env, [], { mode: "hash" });
|
||||
expect(router["routeToPath"]({ path: "/abc" } as Route, {})).toBe("#/abc");
|
||||
expect(router["routeToPath"]({ path: "/abc/def" } as Route, {})).toBe("#/abc/def");
|
||||
expect(router["routeToPath"]({ path: "/abc" } as Route, { val: 12 })).toBe("#/abc");
|
||||
});
|
||||
|
||||
test("simple parameterized path, mode=hash", () => {
|
||||
router = new TestRouter(env, [], { mode: "hash" });
|
||||
expect(router["routeToPath"]({ path: "/abc/{{def}}" } as Route, { def: 34 })).toBe("#/abc/34");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,26 +98,60 @@ describe("destToPath", () => {
|
||||
});
|
||||
|
||||
describe("getRouteParams", () => {
|
||||
const getRouteParams = Router.prototype["getRouteParams"];
|
||||
test("properly match simple routes", () => {
|
||||
router = new TestRouter(env, []);
|
||||
// simple route
|
||||
expect(getRouteParams({path: "/home"} as Route, "/home")).toEqual({});
|
||||
expect(router["getRouteParams"]({ path: "/home" } as Route, "/home")).toEqual({});
|
||||
|
||||
// no match
|
||||
expect(getRouteParams({path: "/home"} as Route, "/otherpath")).toEqual(false);
|
||||
expect(router["getRouteParams"]({ path: "/home" } as Route, "/otherpath")).toEqual(false);
|
||||
|
||||
// fallback route
|
||||
expect(getRouteParams({path: "*"} as Route, "somepath")).toEqual({});
|
||||
expect(router["getRouteParams"]({ path: "*" } as Route, "somepath")).toEqual({});
|
||||
});
|
||||
|
||||
test("properly match simple routes, mode hash", () => {
|
||||
router = new TestRouter(env, [], { mode: "hash" });
|
||||
// simple route
|
||||
expect(router["getRouteParams"]({ path: "/home" } as Route, "#/home")).toEqual({});
|
||||
|
||||
// no match
|
||||
expect(router["getRouteParams"]({ path: "/home" } as Route, "#/otherpath")).toEqual(false);
|
||||
|
||||
// fallback route
|
||||
expect(router["getRouteParams"]({ path: "*" } as Route, "#/somepath")).toEqual({});
|
||||
});
|
||||
|
||||
test("match some parameterized routes", () => {
|
||||
expect(getRouteParams({path: "/invoices/{{id}}"} as Route, "/invoices/3")).toEqual({
|
||||
router = new TestRouter(env, []);
|
||||
expect(router["getRouteParams"]({ path: "/invoices/{{id}}" } as Route, "/invoices/3")).toEqual({
|
||||
id: "3"
|
||||
});
|
||||
});
|
||||
|
||||
test("match some parameterized routes, mode hash", () => {
|
||||
router = new TestRouter(env, [], { mode: "hash" });
|
||||
expect(router["getRouteParams"]({ path: "/invoices/{{id}}" } as Route, "#/invoices/3")).toEqual(
|
||||
{
|
||||
id: "3"
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("can convert to number if needed", () => {
|
||||
expect(getRouteParams({path: "/invoices/{{id.number}}"} as Route, "/invoices/3")).toEqual({
|
||||
router = new TestRouter(env, []);
|
||||
expect(
|
||||
router["getRouteParams"]({ path: "/invoices/{{id.number}}" } as Route, "/invoices/3")
|
||||
).toEqual({
|
||||
id: 3
|
||||
});
|
||||
});
|
||||
|
||||
test("can convert to number if needed, mode: hash", () => {
|
||||
router = new TestRouter(env, [], { mode: "hash" });
|
||||
expect(
|
||||
router["getRouteParams"]({ path: "/invoices/{{id.number}}" } as Route, "#/invoices/3")
|
||||
).toEqual({
|
||||
id: 3
|
||||
});
|
||||
});
|
||||
@@ -131,9 +205,9 @@ describe("beforeRouteEnter", () => {
|
||||
expect(window.location.pathname).toBe("/");
|
||||
const guard = jest.fn(() => false);
|
||||
router = new TestRouter(env, [
|
||||
{ name: "routea", path: "/some/patha"},
|
||||
{ name: "routeb", path: "/some/pathb", beforeRouteEnter: guard }
|
||||
]);
|
||||
{ name: "routea", path: "/some/patha" },
|
||||
{ name: "routeb", path: "/some/pathb", beforeRouteEnter: guard }
|
||||
]);
|
||||
|
||||
await router.start();
|
||||
await router.navigate({ to: "routea" });
|
||||
@@ -147,12 +221,14 @@ describe("beforeRouteEnter", () => {
|
||||
|
||||
test("navigation is redirected if guard decides so", async () => {
|
||||
expect(window.location.pathname).toBe("/");
|
||||
const guard = jest.fn(() => {return {to: "routec"}});
|
||||
const guard = jest.fn(() => {
|
||||
return { to: "routec" };
|
||||
});
|
||||
router = new TestRouter(env, [
|
||||
{ name: "routea", path: "/some/patha"},
|
||||
{ name: "routeb", path: "/some/pathb", beforeRouteEnter: guard },
|
||||
{ name: "routec", path: "/some/pathc"},
|
||||
]);
|
||||
{ name: "routea", path: "/some/patha" },
|
||||
{ name: "routeb", path: "/some/pathb", beforeRouteEnter: guard },
|
||||
{ name: "routec", path: "/some/pathc" }
|
||||
]);
|
||||
|
||||
await router.start();
|
||||
const result = await router.navigate({ to: "routea" });
|
||||
@@ -166,11 +242,13 @@ describe("beforeRouteEnter", () => {
|
||||
|
||||
test("navigation is initially redirected if guard decides so", async () => {
|
||||
expect(window.location.pathname).toBe("/");
|
||||
const guard = jest.fn(() => {return {to: "otherroute"}});
|
||||
const guard = jest.fn(() => {
|
||||
return { to: "otherroute" };
|
||||
});
|
||||
router = new TestRouter(env, [
|
||||
{ name: "landing", path: "/", beforeRouteEnter: guard},
|
||||
{ name: "otherroute", path: "/some/other/route"}
|
||||
]);
|
||||
{ name: "landing", path: "/", beforeRouteEnter: guard },
|
||||
{ name: "otherroute", path: "/some/other/route" }
|
||||
]);
|
||||
|
||||
expect(window.location.pathname).toBe("/");
|
||||
|
||||
|
||||
@@ -15,3 +15,7 @@ exports[`connecting a component to store deep and shallow connecting a component
|
||||
exports[`connecting a component to store deep and shallow connecting a component 3`] = `"<div><span>Bertinchamps</span></div>"`;
|
||||
|
||||
exports[`connecting a component to store deep and shallow connecting a component 4`] = `"<div><span>Kasteel</span></div>"`;
|
||||
|
||||
exports[`various scenarios scenarios with async store updates and some components events 1`] = `"<div><button>Do stuff</button><div><span>Attachment 100</span><span>Name: text.txt</span></div></div>"`;
|
||||
|
||||
exports[`various scenarios scenarios with async store updates and some components events 2`] = `"<div><button>Do stuff</button></div>"`;
|
||||
|
||||
@@ -616,8 +616,8 @@ describe("connecting a component to store", () => {
|
||||
return { flag: s.flag, someId: s.someId };
|
||||
}
|
||||
async render(force) {
|
||||
await def;
|
||||
return super.render(force);
|
||||
await def;
|
||||
return super.render(force);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,7 +628,7 @@ describe("connecting a component to store", () => {
|
||||
}
|
||||
}
|
||||
|
||||
const state = { someId: 1, flag: true, messages: {1: "abc"}};
|
||||
const state = { someId: 1, flag: true, messages: { 1: "abc" } };
|
||||
const actions = {
|
||||
setFlagToFalse({ state }) {
|
||||
state.flag = false;
|
||||
@@ -1040,7 +1040,7 @@ describe("connected components and default values", () => {
|
||||
super.off(eventType, owner);
|
||||
}
|
||||
}
|
||||
const store = new TestStore({ state: {val: 1} });
|
||||
const store = new TestStore({ state: { val: 1 } });
|
||||
(<any>env).store = store;
|
||||
const parent = new Parent(env);
|
||||
|
||||
@@ -1088,9 +1088,91 @@ describe("connected components and default values", () => {
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>0</div>");
|
||||
|
||||
const res = app.dispatch('inc');
|
||||
const res = app.dispatch("inc");
|
||||
expect(res).toBe(1);
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div>1</div>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("various scenarios", () => {
|
||||
let fixture: HTMLElement;
|
||||
let env: Env;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
env = makeTestEnv();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.remove();
|
||||
});
|
||||
|
||||
test("scenarios with async store updates and some components events", async () => {
|
||||
const actions = {
|
||||
async deleteAttachment({ state }) {
|
||||
await Promise.resolve();
|
||||
delete state.attachments[100];
|
||||
state.messages[10].attachmentIds = [];
|
||||
}
|
||||
};
|
||||
const state = {
|
||||
attachments: {
|
||||
100: {
|
||||
id: 100,
|
||||
name: "text.txt"
|
||||
}
|
||||
},
|
||||
messages: {
|
||||
10: {
|
||||
attachmentIds: [100],
|
||||
id: 10
|
||||
}
|
||||
}
|
||||
};
|
||||
const store = new Store({ actions, state });
|
||||
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Message">
|
||||
<button t-on-click="doStuff">Do stuff</button>
|
||||
<Attachment t-foreach="storeProps.attachmentIds" t-key="attachmentId" t-as="attachmentId" id="attachmentId"/>
|
||||
</div>
|
||||
<div t-name="Attachment">
|
||||
<span>Attachment <t t-esc="props.id"/></span>
|
||||
<span>Name: <t t-esc="storeProps.name"/></span>
|
||||
</div>
|
||||
</templates>
|
||||
`);
|
||||
class Attachment extends ConnectedComponent<any, any, any> {
|
||||
static mapStoreToProps(state, ownProps) {
|
||||
return {
|
||||
name: state.attachments[ownProps.id].name
|
||||
};
|
||||
}
|
||||
}
|
||||
class Message extends ConnectedComponent<any, any, any> {
|
||||
static mapStoreToProps(state) {
|
||||
return {
|
||||
attachmentIds: state.messages[10].attachmentIds
|
||||
};
|
||||
}
|
||||
components = { Attachment };
|
||||
state = { isAttachmentDeleted: false };
|
||||
doStuff() {
|
||||
this.dispatch("deleteAttachment", 100);
|
||||
this.state.isAttachmentDeleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
(<any>env).store = store;
|
||||
const message = new Message(env);
|
||||
await message.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toMatchSnapshot();
|
||||
|
||||
fixture.querySelector("button")!.click();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
+24
-24
@@ -249,32 +249,32 @@ describe("advanced state properties", () => {
|
||||
expect(store.state.a).toEqual([1, 2, 3, 53]);
|
||||
});
|
||||
|
||||
test("can use object assign in store", async () => {
|
||||
const actions = {
|
||||
dosomething({ state }) {
|
||||
Object.assign(state.westmalle, { a: 3, b: 4 });
|
||||
}
|
||||
};
|
||||
const store = new Store({
|
||||
state: { westmalle: { a: 1, b: 2 } },
|
||||
actions
|
||||
});
|
||||
store.dispatch("dosomething");
|
||||
expect(store.state.westmalle).toEqual({ a: 3, b: 4 });
|
||||
test("can use object assign in store", async () => {
|
||||
const actions = {
|
||||
dosomething({ state }) {
|
||||
Object.assign(state.westmalle, { a: 3, b: 4 });
|
||||
}
|
||||
};
|
||||
const store = new Store({
|
||||
state: { westmalle: { a: 1, b: 2 } },
|
||||
actions
|
||||
});
|
||||
store.dispatch("dosomething");
|
||||
expect(store.state.westmalle).toEqual({ a: 3, b: 4 });
|
||||
});
|
||||
|
||||
test("aku reactive store state 1", async () => {
|
||||
const actions = {
|
||||
inc({ state }) {
|
||||
state.counter++;
|
||||
}
|
||||
};
|
||||
const state = { counter: 0 };
|
||||
const store = new Store({ state, actions });
|
||||
expect(store.state.counter).toBe(0);
|
||||
store.dispatch("inc", {});
|
||||
expect(store.state.counter).toBe(1);
|
||||
});
|
||||
test("aku reactive store state 1", async () => {
|
||||
const actions = {
|
||||
inc({ state }) {
|
||||
state.counter++;
|
||||
}
|
||||
};
|
||||
const state = { counter: 0 };
|
||||
const store = new Store({ state, actions });
|
||||
expect(store.state.counter).toBe(0);
|
||||
store.dispatch("inc", {});
|
||||
expect(store.state.counter).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updates triggered by the store", () => {
|
||||
|
||||
Reference in New Issue
Block a user