mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] qweb: t-props directive
We reimplement the directive "t-props" and add some tests for it.
This commit is contained in:
committed by
Géry Debongnie
parent
700d574314
commit
ba2fe3ff55
+10
-1
@@ -905,7 +905,16 @@ export class QWebCompiler {
|
||||
for (let p in ast.props) {
|
||||
props.push(`${p}: ${compileExpr(ast.props[p]) || undefined}`);
|
||||
}
|
||||
const propString = `{${props.join(",")}}`;
|
||||
const propStr = `{${props.join(",")}}`;
|
||||
|
||||
let propString = propStr;
|
||||
if (ast.dynamicProps) {
|
||||
if (!props.length) {
|
||||
propString = `${compileExpr(ast.dynamicProps)}`;
|
||||
} else {
|
||||
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}, ${propStr})`;
|
||||
}
|
||||
}
|
||||
|
||||
// cmap key
|
||||
const key = this.generateComponentKey();
|
||||
|
||||
+5
-1
@@ -105,6 +105,7 @@ export interface ASTComponent {
|
||||
type: ASTType.TComponent;
|
||||
name: string;
|
||||
isDynamic: boolean;
|
||||
dynamicProps: string | null;
|
||||
props: { [name: string]: string };
|
||||
handlers: { [event: string]: string };
|
||||
slots: { [name: string]: AST };
|
||||
@@ -657,6 +658,9 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
||||
node.removeAttribute("t-component");
|
||||
}
|
||||
|
||||
const dynamicProps = node.getAttribute("t-props");
|
||||
node.removeAttribute("t-props");
|
||||
|
||||
const props: ASTComponent["props"] = {};
|
||||
const handlers: ASTComponent["handlers"] = {};
|
||||
for (let name of node.getAttributeNames()) {
|
||||
@@ -706,7 +710,7 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
||||
slots.default = defaultContent;
|
||||
}
|
||||
}
|
||||
return { type: ASTType.TComponent, name, isDynamic, props, handlers, slots };
|
||||
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, handlers, slots };
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`t-props t-props and other props 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div><block-text-0/><block-text-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let d1 = ctx['props'].a;
|
||||
let d2 = ctx['props'].b;
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-props t-props and other props 2`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div><div><block-child-0/></div></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2 = component(\`Comp\`, Object.assign({}, ctx['state1'], {a: ctx['a']}), key + \`__1\`, node, ctx);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-props t-props only 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let d1 = ctx['props'].a;
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-props t-props only 2`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div><div><block-child-0/></div></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2 = component(\`Comp\`, ctx['state'], key + \`__1\`, node, ctx);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -914,8 +914,9 @@ describe.skip("mount special cases", () => {
|
||||
// expect(scheduler.tasks.length).toBe(0);
|
||||
// expect(error).toBeDefined();
|
||||
// expect(error.message).toBe("Cannot mount a destroyed component");
|
||||
// });
|
||||
// test("destroying a sub-component cleans itself from parent's vnode", async () => {
|
||||
});
|
||||
|
||||
test("destroying a sub-component cleans itself from parent's vnode", async () => {
|
||||
// class C1 extends Component {
|
||||
// static template = xml`<div><div><t t-esc="props.a"/></div></div>`;
|
||||
// }
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { App, Component, mount, onWillStart, onWillUpdateProps, useState } from "../../src";
|
||||
import { Fiber } from "../../src/component/fibers";
|
||||
import { onMounted, onPatched, onWillPatch, onWillUnmount } from "../../src/component/lifecycle_hooks";
|
||||
import {
|
||||
onMounted,
|
||||
onPatched,
|
||||
onWillPatch,
|
||||
onWillUnmount,
|
||||
} from "../../src/component/lifecycle_hooks";
|
||||
import { Scheduler } from "../../src/component/scheduler";
|
||||
import { status } from "../../src/component/status";
|
||||
import { xml } from "../../src/tags";
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Component, mount, useState, xml } from "../../src";
|
||||
import { makeTestFixture, nextTick, snapshotEverything } from "../helpers";
|
||||
|
||||
snapshotEverything();
|
||||
|
||||
let fixture: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
});
|
||||
|
||||
describe("t-props", () => {
|
||||
test("t-props only", async () => {
|
||||
class Comp extends Component {
|
||||
static template = xml`<div><t t-esc="props.a"/></div>`;
|
||||
}
|
||||
class Parent extends Component {
|
||||
static components = { Comp };
|
||||
static template = xml`<div><div><Comp t-props="state"/></div></div>`;
|
||||
state = useState({
|
||||
a: "first",
|
||||
});
|
||||
}
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.textContent).toBe("first");
|
||||
parent.state.a = "";
|
||||
await nextTick();
|
||||
expect(fixture.textContent).toBe("");
|
||||
parent.state.a = "fixed";
|
||||
await nextTick();
|
||||
expect(fixture.textContent).toBe("fixed");
|
||||
});
|
||||
|
||||
test("t-props and other props", async () => {
|
||||
class Comp extends Component {
|
||||
static template = xml`<div><t t-esc="props.a"/><t t-esc="props.b"/></div>`;
|
||||
}
|
||||
class Parent extends Component {
|
||||
static components = { Comp };
|
||||
static template = xml`<div><div><Comp t-props="state1" a="a"/></div></div>`;
|
||||
state1 = useState({
|
||||
a: "first",
|
||||
b: "second",
|
||||
});
|
||||
a = "third";
|
||||
}
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.textContent).toBe("thirdsecond");
|
||||
delete parent.a;
|
||||
parent.render();
|
||||
await nextTick();
|
||||
expect(fixture.textContent).toBe("second");
|
||||
});
|
||||
});
|
||||
@@ -699,6 +699,7 @@ describe("qweb parser", () => {
|
||||
isDynamic: false,
|
||||
name: "Comp",
|
||||
handlers: {},
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
slots: {},
|
||||
},
|
||||
@@ -824,6 +825,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(`<MyComponent />`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: {},
|
||||
slots: {},
|
||||
@@ -835,6 +837,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(`<MyComponent a="1" b="'b'"/>`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: { a: "1", b: "'b'" },
|
||||
handlers: {},
|
||||
isDynamic: false,
|
||||
@@ -842,10 +845,23 @@ describe("qweb parser", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("component with t-props", async () => {
|
||||
expect(parse(`<MyComponent t-props="state" a="1"/>`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: "state",
|
||||
props: { a: "1" },
|
||||
handlers: {},
|
||||
isDynamic: false,
|
||||
slots: {},
|
||||
});
|
||||
});
|
||||
|
||||
test("component with event handler", async () => {
|
||||
expect(parse(`<MyComponent t-on-click="someMethod"/>`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: { click: "someMethod" },
|
||||
isDynamic: false,
|
||||
@@ -857,6 +873,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(`<MyComponent>foo</MyComponent>`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
isDynamic: false,
|
||||
handlers: {},
|
||||
@@ -869,6 +886,7 @@ describe("qweb parser", () => {
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
isDynamic: false,
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: {},
|
||||
slots: {
|
||||
@@ -888,6 +906,7 @@ describe("qweb parser", () => {
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
isDynamic: false,
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: {},
|
||||
slots: { name: { type: ASTType.Text, value: "foo" } },
|
||||
@@ -898,6 +917,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(`<MyComponent><t t-set-slot="name">foo</t> </MyComponent>`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
isDynamic: false,
|
||||
handlers: {},
|
||||
@@ -917,6 +937,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(template)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: {},
|
||||
isDynamic: false,
|
||||
@@ -931,6 +952,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(`<t t-component="myComponent" />`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "myComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: {},
|
||||
isDynamic: true,
|
||||
@@ -942,6 +964,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(`<t t-component="mycomponent" a="1" b="'b'"/>`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "mycomponent",
|
||||
dynamicProps: null,
|
||||
props: { a: "1", b: "'b'" },
|
||||
handlers: {},
|
||||
isDynamic: true,
|
||||
@@ -949,10 +972,23 @@ describe("qweb parser", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("dynamic component with t-props", async () => {
|
||||
expect(parse(`<t t-component="mycomponent" t-props="state" a="1"/>`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "mycomponent",
|
||||
dynamicProps: "state",
|
||||
props: { a: "1" },
|
||||
handlers: {},
|
||||
isDynamic: true,
|
||||
slots: {},
|
||||
});
|
||||
});
|
||||
|
||||
test("component with t-esc", async () => {
|
||||
expect(parse(`<MyComponent t-esc="someValue"/>`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: {},
|
||||
isDynamic: false,
|
||||
@@ -964,6 +1000,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(`<MyComponent t-call="subTemplate"/>`)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: {},
|
||||
isDynamic: false,
|
||||
@@ -982,6 +1019,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(template)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: {},
|
||||
isDynamic: false,
|
||||
@@ -991,6 +1029,7 @@ describe("qweb parser", () => {
|
||||
isDynamic: false,
|
||||
handlers: {},
|
||||
name: "Child",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
slots: { brol: { type: ASTType.Text, value: "coucou" } },
|
||||
},
|
||||
@@ -1009,6 +1048,7 @@ describe("qweb parser", () => {
|
||||
expect(parse(template)).toEqual({
|
||||
type: ASTType.TComponent,
|
||||
name: "MyComponent",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
handlers: {},
|
||||
isDynamic: false,
|
||||
@@ -1018,6 +1058,7 @@ describe("qweb parser", () => {
|
||||
isDynamic: false,
|
||||
handlers: {},
|
||||
name: "Child",
|
||||
dynamicProps: null,
|
||||
props: {},
|
||||
slots: { brol: { type: ASTType.Text, value: "coucou" } },
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user