Skip to content

Commit

Permalink
[IMP] app: add global values at compile time
Browse files Browse the repository at this point in the history
This commit, add a new configuration on the App: globalValues.
It's a global object of elements available at compilations.

For instance:
```js
    const app = new App(SomeComponent, {
      globalValues: {
        plop: (string: any) => {
          steps.push(string);
        },
      },
    });
```

The plop function will be available at the compilation, so it can be
used on the templates :
```xml
<div t-on-click="() => __globals__.plop('click')" class="my-div"/>
```
  • Loading branch information
jpp-odoo committed Nov 21, 2024
1 parent e9a1886 commit a96815f
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 2 deletions.
1 change: 1 addition & 0 deletions doc/reference/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ The `config` object is an object with some of the following keys:
whenever it encounters a component that does not provide a [static props description](props.md#props-validation).
- **`customDirectives (object)`**: if given, the corresponding function on the object will be called
on the template custom directives: `t-custom-*` (see [Custom Directives](templates.md#custom-directives)).
- **`globalValues (object)`**: Global object of elements available at compilations.

## `mount` helper

Expand Down
4 changes: 4 additions & 0 deletions src/compiler/code_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface Config {
export interface CodeGenOptions extends Config {
hasSafeContext?: boolean;
name?: string;
globalValues?: object;
}

// using a non-html document so that <inner/outer>HTML serializes as XML instead
Expand Down Expand Up @@ -286,6 +287,9 @@ export class CodeGenerator {
this.dev = options.dev || false;
this.ast = ast;
this.templateName = options.name;
if (options.globalValues && Object.keys(options.globalValues).length) {
this.helpers.add("__globals__");
}
}

generateCode(): string {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type TemplateFunction = (app: TemplateSet, bdom: any, helpers: any) => Te
interface CompileOptions extends Config {
name?: string;
customDirectives?: customDirectives;
globalValues?: object;
}
export function compile(
template: string | Element,
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/inline_expressions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { OwlError } from "../common/owl_error";
//------------------------------------------------------------------------------

const RESERVED_WORDS =
"true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,eval,void,Math,RegExp,Array,Object,Date".split(
"true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,eval,void,Math,RegExp,Array,Object,Date,__globals__".split(
","
);

Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(
translateFn: this.translateFn,
translatableAttributes: this.translatableAttributes,
customDirectives: this.customDirectives,
globalValues: this.globalValues,
});
};
6 changes: 5 additions & 1 deletion src/runtime/template_set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface TemplateSetConfig {
templates?: string | Document | Record<string, string>;
getTemplate?: (s: string) => Element | Function | string | void;
customDirectives?: customDirectives;
globalValues?: object;
}

export class TemplateSet {
Expand All @@ -30,6 +31,7 @@ export class TemplateSet {
translatableAttributes?: string[];
Portal = Portal;
customDirectives: customDirectives;
globalValues: object;

constructor(config: TemplateSetConfig = {}) {
this.dev = config.dev || false;
Expand All @@ -46,6 +48,7 @@ export class TemplateSet {
}
this.getRawTemplate = config.getTemplate;
this.customDirectives = config.customDirectives || {};
this.globalValues = config.globalValues || {};
}

addTemplate(name: string, template: string | Element) {
Expand Down Expand Up @@ -101,7 +104,8 @@ export class TemplateSet {
this.templates[name] = function (context, parent) {
return templates[name].call(this, context, parent);
};
const template = templateFn(this, bdom, helpers);
const runtimeUtils = { ...helpers, __globals__: this.globalValues };
const template = templateFn(this, bdom, runtimeUtils);
this.templates[name] = template;
}
return this.templates[name];
Expand Down
15 changes: 15 additions & 0 deletions tests/app/__snapshots__/app.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ exports[`app app: clear scheduler tasks and destroy cancelled nodes immediately
}"
`;
exports[`app can add functions to the bdom 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { __globals__ } = helpers;
let block1 = createBlock(\`<div class=\\"my-div\\" block-handler-0=\\"click\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let hdlr1 = [()=>__globals__.plop('click'), ctx];
return block1([hdlr1]);
}
}"
`;
exports[`app can call processTask twice in a row without crashing 1`] = `
"function anonymous(app, bdom, helpers
) {
Expand Down
18 changes: 18 additions & 0 deletions tests/app/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,22 @@ describe("app", () => {
await app.mount(fixture);
expect(fixture.innerHTML).toBe("parent<div></div>");
});

test("can add functions to the bdom", async () => {
const steps: string[] = [];
class SomeComponent extends Component {
static template = xml`<div t-on-click="() => __globals__.plop('click')" class="my-div"/>`;
}
const app = new App(SomeComponent, {
globalValues: {
plop: (string: any) => {
steps.push(string);
},
},
});
await app.mount(fixture);
expect(fixture.innerHTML).toBe(`<div class="my-div"></div>`);
fixture.querySelector("div")!.click();
expect(steps).toEqual(["click"]);
});
});

0 comments on commit a96815f

Please sign in to comment.