mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] component: add .bind suffix to props for easy binding
This commit is contained in:
committed by
Samuel Degueldre
parent
7b95315b87
commit
4cde06d685
@@ -124,6 +124,23 @@ export function safeOutput(value: any): ReturnType<typeof toggler> {
|
||||
return toggler(safeKey, block);
|
||||
}
|
||||
|
||||
let boundFunctions = new WeakMap();
|
||||
|
||||
function bind(ctx: any, fn: Function): Function {
|
||||
let component = ctx.__owl__.component;
|
||||
let boundFnMap = boundFunctions.get(component);
|
||||
if (!boundFnMap) {
|
||||
boundFnMap = new WeakMap();
|
||||
boundFunctions.set(component, boundFnMap);
|
||||
}
|
||||
let boundFn = boundFnMap.get(fn);
|
||||
if (!boundFn) {
|
||||
boundFn = fn.bind(component);
|
||||
boundFnMap.set(fn, boundFn);
|
||||
}
|
||||
return boundFn;
|
||||
}
|
||||
|
||||
export const UTILS = {
|
||||
withDefault,
|
||||
zero: Symbol("zero"),
|
||||
@@ -137,4 +154,5 @@ export const UTILS = {
|
||||
toNumber,
|
||||
validateProps,
|
||||
safeOutput,
|
||||
bind,
|
||||
};
|
||||
|
||||
@@ -975,10 +975,19 @@ export class CodeGenerator {
|
||||
// props
|
||||
const props: string[] = [];
|
||||
let hasSlotsProp = false;
|
||||
for (let p in ast.props) {
|
||||
const propName = /^[a-z_]+$/i.test(p) ? p : `'${p}'`;
|
||||
props.push(`${propName}: ${this.captureExpression(ast.props[p]) || undefined}`);
|
||||
if (p === "slots") {
|
||||
for (let propName in ast.props) {
|
||||
let propValue = this.captureExpression(ast.props[propName]) || undefined;
|
||||
if (propName.includes(".")) {
|
||||
let [name, suffix] = propName.split(".");
|
||||
if (suffix === "bind") {
|
||||
this.helpers.add("bind");
|
||||
propName = name;
|
||||
propValue = `bind(ctx, ${propValue})`;
|
||||
}
|
||||
}
|
||||
propName = /^[a-z_]+$/i.test(propName) ? propName : `'${propName}'`;
|
||||
props.push(`${propName}: ${propValue}`);
|
||||
if (propName === "slots") {
|
||||
hasSlotsProp = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,3 +262,49 @@ exports[`basics template string in prop 2`] = `
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`bound functions is referentially equal after update 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return text(ctx['props'].val);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`bound functions is referentially equal after update 2`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||
let { bind } = helpers;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return component(\`Child\`, {val: ctx['state'].val,fn: bind(ctx, ctx['someFunction'])}, key + \`__1\`, node, ctx);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`can bind function prop with bind suffix 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return text(\`child\`);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`can bind function prop with bind suffix 2`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||
let { bind } = helpers;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return component(\`Child\`, {doSomething: bind(ctx, ctx['doSomething'])}, key + \`__1\`, node, ctx);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, mount, useState } from "../../src";
|
||||
import { Component, mount, onWillUpdateProps, useState } from "../../src";
|
||||
import { xml } from "../../src/tags";
|
||||
import { makeTestFixture, snapshotEverything } from "../helpers";
|
||||
import { makeTestFixture, nextTick, snapshotEverything } from "../helpers";
|
||||
|
||||
let fixture: HTMLElement;
|
||||
|
||||
@@ -176,3 +176,53 @@ describe("basics", () => {
|
||||
await mount(Parent, fixture);
|
||||
});
|
||||
});
|
||||
|
||||
test("can bind function prop with bind suffix", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`child`;
|
||||
setup() {
|
||||
this.props.doSomething(123);
|
||||
}
|
||||
}
|
||||
|
||||
let boundedThing: any = null;
|
||||
|
||||
class Parent extends Component {
|
||||
static template = xml`<Child doSomething.bind="doSomething"/>`;
|
||||
static components = { Child };
|
||||
|
||||
doSomething(val: number) {
|
||||
expect(val).toBe(123);
|
||||
boundedThing = this;
|
||||
}
|
||||
}
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(boundedThing).toBe(parent);
|
||||
expect(fixture.innerHTML).toBe("child");
|
||||
});
|
||||
|
||||
test("bound functions is referentially equal after update", async () => {
|
||||
let isEqual = false;
|
||||
class Child extends Component {
|
||||
static template = xml`<t t-esc="props.val"/>`;
|
||||
setup() {
|
||||
onWillUpdateProps((nextProps: any) => {
|
||||
isEqual = nextProps.fn === this.props.fn;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static template = xml`<Child val="state.val" fn.bind="someFunction"/>`;
|
||||
static components = { Child };
|
||||
state = useState({ val: 1 });
|
||||
someFunction() {}
|
||||
}
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
parent.state.val = 3;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("3");
|
||||
expect(isEqual).toBe(true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user