mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 68f491cd32 | |||
| 7b3e39ba27 | |||
| 61fc3f4fdc | |||
| 7b454dae66 | |||
| 70101e4c66 | |||
| 5ef405293a |
@@ -61,6 +61,8 @@ The `config` object is an object with some of the following keys:
|
||||
templates (see [translations](translations.md))
|
||||
- **`templates (string | xml document)`**: all the templates that will be used by
|
||||
the components created by the application.
|
||||
- **`getTemplate ((s: string) => Element | Function | string | void)`**: a function that will be called by owl when it
|
||||
needs a template. If undefined is returned, owl looks into the app templates.
|
||||
- **`warnIfNoStaticProps (boolean, default=false)`**: if true, Owl will log a warning
|
||||
whenever it encounters a component that does not provide a [static props description](props.md#props-validation).
|
||||
|
||||
|
||||
+25
-49
@@ -1850,8 +1850,9 @@ const NO_CALLBACK = () => {
|
||||
};
|
||||
const objectToString = Object.prototype.toString;
|
||||
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
const SUPPORTED_RAW_TYPES = new Set(["Object", "Array", "Set", "Map", "WeakMap"]);
|
||||
const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]);
|
||||
// Use arrays because Array.includes is faster than Set.has for small arrays
|
||||
const SUPPORTED_RAW_TYPES = ["Object", "Array", "Set", "Map", "WeakMap"];
|
||||
const COLLECTION_RAW_TYPES = ["Set", "Map", "WeakMap"];
|
||||
/**
|
||||
* extract "RawType" from strings like "[object RawType]" => this lets us ignore
|
||||
* many native objects such as Promise (whose toString is [object Promise])
|
||||
@@ -1874,7 +1875,7 @@ function canBeMadeReactive(value) {
|
||||
if (typeof value !== "object") {
|
||||
return false;
|
||||
}
|
||||
return SUPPORTED_RAW_TYPES.has(rawType(value));
|
||||
return SUPPORTED_RAW_TYPES.includes(rawType(value));
|
||||
}
|
||||
/**
|
||||
* Creates a reactive from the given object/callback if possible and returns it,
|
||||
@@ -2044,7 +2045,7 @@ function reactive(target, callback = NO_CALLBACK) {
|
||||
const reactivesForTarget = reactiveCache.get(target);
|
||||
if (!reactivesForTarget.has(callback)) {
|
||||
const targetRawType = rawType(target);
|
||||
const handler = COLLECTION_RAWTYPES.has(targetRawType)
|
||||
const handler = COLLECTION_RAW_TYPES.includes(targetRawType)
|
||||
? collectionsProxyHandler(target, callback, targetRawType)
|
||||
: basicProxyHandler(callback);
|
||||
const proxy = new Proxy(target, handler);
|
||||
@@ -3159,8 +3160,14 @@ const helpers = {
|
||||
makeRefWrapper,
|
||||
};
|
||||
|
||||
const bdom = { text, createBlock, list, multi, html, toggler, comment };
|
||||
function parseXML$1(xml) {
|
||||
/**
|
||||
* Parses an XML string into an XML document, throwing errors on parser errors
|
||||
* instead of returning an XML document containing the parseerror.
|
||||
*
|
||||
* @param xml the string to parse
|
||||
* @returns an XML document corresponding to the content of the string
|
||||
*/
|
||||
function parseXML(xml) {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(xml, "text/xml");
|
||||
if (doc.getElementsByTagName("parsererror").length) {
|
||||
@@ -3187,7 +3194,9 @@ function parseXML$1(xml) {
|
||||
throw new OwlError(msg);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
|
||||
const bdom = { text, createBlock, list, multi, html, toggler, comment };
|
||||
class TemplateSet {
|
||||
constructor(config = {}) {
|
||||
this.rawTemplates = Object.create(globalTemplates);
|
||||
@@ -3206,6 +3215,7 @@ class TemplateSet {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.getRawTemplate = config.getTemplate;
|
||||
}
|
||||
static registerTemplate(name, fn) {
|
||||
globalTemplates[name] = fn;
|
||||
@@ -3235,15 +3245,16 @@ class TemplateSet {
|
||||
// empty string
|
||||
return;
|
||||
}
|
||||
xml = xml instanceof Document ? xml : parseXML$1(xml);
|
||||
xml = xml instanceof Document ? xml : parseXML(xml);
|
||||
for (const template of xml.querySelectorAll("[t-name]")) {
|
||||
const name = template.getAttribute("t-name");
|
||||
this.addTemplate(name, template);
|
||||
}
|
||||
}
|
||||
getTemplate(name) {
|
||||
var _a;
|
||||
if (!(name in this.templates)) {
|
||||
const rawTemplate = this.rawTemplates[name];
|
||||
const rawTemplate = ((_a = this.getRawTemplate) === null || _a === void 0 ? void 0 : _a.call(this, name)) || this.rawTemplates[name];
|
||||
if (rawTemplate === undefined) {
|
||||
let extraInfo = "";
|
||||
try {
|
||||
@@ -4953,9 +4964,9 @@ function parseDOMNode(node, ctx) {
|
||||
const isSelect = tagName === "select";
|
||||
const isCheckboxInput = isInput && typeAttr === "checkbox";
|
||||
const isRadioInput = isInput && typeAttr === "radio";
|
||||
const hasLazyMod = attr.includes(".lazy");
|
||||
const hasNumberMod = attr.includes(".number");
|
||||
const hasTrimMod = attr.includes(".trim");
|
||||
const hasLazyMod = hasTrimMod || attr.includes(".lazy");
|
||||
const hasNumberMod = attr.includes(".number");
|
||||
const eventType = isRadioInput ? "click" : isSelect || hasLazyMod ? "change" : "input";
|
||||
model = {
|
||||
baseExpr,
|
||||
@@ -5501,41 +5512,6 @@ function normalizeTEscTOut(el) {
|
||||
function normalizeXML(el) {
|
||||
normalizeTIf(el);
|
||||
normalizeTEscTOut(el);
|
||||
}
|
||||
/**
|
||||
* Parses an XML string into an XML document, throwing errors on parser errors
|
||||
* instead of returning an XML document containing the parseerror.
|
||||
*
|
||||
* @param xml the string to parse
|
||||
* @returns an XML document corresponding to the content of the string
|
||||
*/
|
||||
function parseXML(xml) {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(xml, "text/xml");
|
||||
if (doc.getElementsByTagName("parsererror").length) {
|
||||
let msg = "Invalid XML in template.";
|
||||
const parsererrorText = doc.getElementsByTagName("parsererror")[0].textContent;
|
||||
if (parsererrorText) {
|
||||
msg += "\nThe parser has produced the following error message:\n" + parsererrorText;
|
||||
const re = /\d+/g;
|
||||
const firstMatch = re.exec(parsererrorText);
|
||||
if (firstMatch) {
|
||||
const lineNumber = Number(firstMatch[0]);
|
||||
const line = xml.split("\n")[lineNumber - 1];
|
||||
const secondMatch = re.exec(parsererrorText);
|
||||
if (line && secondMatch) {
|
||||
const columnIndex = Number(secondMatch[0]) - 1;
|
||||
if (line[columnIndex]) {
|
||||
msg +=
|
||||
`\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\n` +
|
||||
`${line}\n${"-".repeat(columnIndex - 1)}^`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new OwlError(msg);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
function compile(template, options = {}) {
|
||||
@@ -5562,7 +5538,7 @@ function compile(template, options = {}) {
|
||||
}
|
||||
|
||||
// do not modify manually. This file is generated by the release script.
|
||||
const version = "2.2.7";
|
||||
const version = "2.2.9";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Scheduler
|
||||
@@ -5991,6 +5967,6 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
|
||||
export { App, Component, EventBus, OwlError, __info__, blockDom, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml };
|
||||
|
||||
|
||||
__info__.date = '2023-12-06T13:56:01.636Z';
|
||||
__info__.hash = 'e94428a';
|
||||
__info__.date = '2024-01-12T14:43:56.804Z';
|
||||
__info__.hash = '7b3e39b';
|
||||
__info__.url = 'https://github.com/odoo/owl';
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@odoo/owl",
|
||||
"version": "2.2.7",
|
||||
"version": "2.2.9",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@odoo/owl",
|
||||
"version": "2.2.7",
|
||||
"version": "2.2.9",
|
||||
"description": "Odoo Web Library (OWL)",
|
||||
"main": "dist/owl.cjs.js",
|
||||
"module": "dist/owl.es.js",
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { OwlError } from "./owl_error";
|
||||
|
||||
/**
|
||||
* Parses an XML string into an XML document, throwing errors on parser errors
|
||||
* instead of returning an XML document containing the parseerror.
|
||||
*
|
||||
* @param xml the string to parse
|
||||
* @returns an XML document corresponding to the content of the string
|
||||
*/
|
||||
export function parseXML(xml: string): XMLDocument {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(xml, "text/xml");
|
||||
if (doc.getElementsByTagName("parsererror").length) {
|
||||
let msg = "Invalid XML in template.";
|
||||
const parsererrorText = doc.getElementsByTagName("parsererror")[0].textContent;
|
||||
if (parsererrorText) {
|
||||
msg += "\nThe parser has produced the following error message:\n" + parsererrorText;
|
||||
const re = /\d+/g;
|
||||
const firstMatch = re.exec(parsererrorText);
|
||||
if (firstMatch) {
|
||||
const lineNumber = Number(firstMatch[0]);
|
||||
const line = xml.split("\n")[lineNumber - 1];
|
||||
const secondMatch = re.exec(parsererrorText);
|
||||
if (line && secondMatch) {
|
||||
const columnIndex = Number(secondMatch[0]) - 1;
|
||||
if (line[columnIndex]) {
|
||||
msg +=
|
||||
`\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\n` +
|
||||
`${line}\n${"-".repeat(columnIndex - 1)}^`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new OwlError(msg);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
+3
-39
@@ -1,4 +1,5 @@
|
||||
import { OwlError } from "../common/owl_error";
|
||||
import { parseXML } from "../common/utils";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// AST Type definition
|
||||
@@ -366,9 +367,9 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
||||
const isSelect = tagName === "select";
|
||||
const isCheckboxInput = isInput && typeAttr === "checkbox";
|
||||
const isRadioInput = isInput && typeAttr === "radio";
|
||||
const hasLazyMod = attr.includes(".lazy");
|
||||
const hasNumberMod = attr.includes(".number");
|
||||
const hasTrimMod = attr.includes(".trim");
|
||||
const hasLazyMod = hasTrimMod || attr.includes(".lazy");
|
||||
const hasNumberMod = attr.includes(".number");
|
||||
const eventType = isRadioInput ? "click" : isSelect || hasLazyMod ? "change" : "input";
|
||||
|
||||
model = {
|
||||
@@ -972,40 +973,3 @@ function normalizeXML(el: Element) {
|
||||
normalizeTIf(el);
|
||||
normalizeTEscTOut(el);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an XML string into an XML document, throwing errors on parser errors
|
||||
* instead of returning an XML document containing the parseerror.
|
||||
*
|
||||
* @param xml the string to parse
|
||||
* @returns an XML document corresponding to the content of the string
|
||||
*/
|
||||
function parseXML(xml: string): XMLDocument {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(xml, "text/xml");
|
||||
if (doc.getElementsByTagName("parsererror").length) {
|
||||
let msg = "Invalid XML in template.";
|
||||
const parsererrorText = doc.getElementsByTagName("parsererror")[0].textContent;
|
||||
if (parsererrorText) {
|
||||
msg += "\nThe parser has produced the following error message:\n" + parsererrorText;
|
||||
const re = /\d+/g;
|
||||
const firstMatch = re.exec(parsererrorText);
|
||||
if (firstMatch) {
|
||||
const lineNumber = Number(firstMatch[0]);
|
||||
const line = xml.split("\n")[lineNumber - 1];
|
||||
const secondMatch = re.exec(parsererrorText);
|
||||
if (line && secondMatch) {
|
||||
const columnIndex = Number(secondMatch[0]) - 1;
|
||||
if (line[columnIndex]) {
|
||||
msg +=
|
||||
`\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\n` +
|
||||
`${line}\n${"-".repeat(columnIndex - 1)}^`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new OwlError(msg);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@ type CollectionRawType = "Set" | "Map" | "WeakMap";
|
||||
const objectToString = Object.prototype.toString;
|
||||
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
const SUPPORTED_RAW_TYPES = new Set(["Object", "Array", "Set", "Map", "WeakMap"]);
|
||||
const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]);
|
||||
// Use arrays because Array.includes is faster than Set.has for small arrays
|
||||
const SUPPORTED_RAW_TYPES = ["Object", "Array", "Set", "Map", "WeakMap"];
|
||||
const COLLECTION_RAW_TYPES = ["Set", "Map", "WeakMap"];
|
||||
|
||||
/**
|
||||
* extract "RawType" from strings like "[object RawType]" => this lets us ignore
|
||||
@@ -45,7 +46,7 @@ function canBeMadeReactive(value: any): boolean {
|
||||
if (typeof value !== "object") {
|
||||
return false;
|
||||
}
|
||||
return SUPPORTED_RAW_TYPES.has(rawType(value));
|
||||
return SUPPORTED_RAW_TYPES.includes(rawType(value));
|
||||
}
|
||||
/**
|
||||
* Creates a reactive from the given object/callback if possible and returns it,
|
||||
@@ -220,7 +221,7 @@ export function reactive<T extends Target>(target: T, callback: Callback = NO_CA
|
||||
const reactivesForTarget = reactiveCache.get(target)!;
|
||||
if (!reactivesForTarget.has(callback)) {
|
||||
const targetRawType = rawType(target);
|
||||
const handler = COLLECTION_RAWTYPES.has(targetRawType)
|
||||
const handler = COLLECTION_RAW_TYPES.includes(targetRawType)
|
||||
? collectionsProxyHandler(target as Collection, callback, targetRawType as CollectionRawType)
|
||||
: basicProxyHandler<T>(callback);
|
||||
const proxy = new Proxy(target, handler as ProxyHandler<T>) as Reactive<T>;
|
||||
|
||||
@@ -4,44 +4,16 @@ import { getCurrent } from "./component_node";
|
||||
import { Portal, portalTemplate } from "./portal";
|
||||
import { helpers } from "./template_helpers";
|
||||
import { OwlError } from "../common/owl_error";
|
||||
import { parseXML } from "../common/utils";
|
||||
|
||||
const bdom = { text, createBlock, list, multi, html, toggler, comment };
|
||||
|
||||
function parseXML(xml: string): Document {
|
||||
const parser = new DOMParser();
|
||||
|
||||
const doc = parser.parseFromString(xml, "text/xml");
|
||||
if (doc.getElementsByTagName("parsererror").length) {
|
||||
let msg = "Invalid XML in template.";
|
||||
const parsererrorText = doc.getElementsByTagName("parsererror")[0].textContent;
|
||||
if (parsererrorText) {
|
||||
msg += "\nThe parser has produced the following error message:\n" + parsererrorText;
|
||||
const re = /\d+/g;
|
||||
const firstMatch = re.exec(parsererrorText);
|
||||
if (firstMatch) {
|
||||
const lineNumber = Number(firstMatch[0]);
|
||||
const line = xml.split("\n")[lineNumber - 1];
|
||||
const secondMatch = re.exec(parsererrorText);
|
||||
if (line && secondMatch) {
|
||||
const columnIndex = Number(secondMatch[0]) - 1;
|
||||
if (line[columnIndex]) {
|
||||
msg +=
|
||||
`\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\n` +
|
||||
`${line}\n${"-".repeat(columnIndex - 1)}^`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new OwlError(msg);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
export interface TemplateSetConfig {
|
||||
dev?: boolean;
|
||||
translatableAttributes?: string[];
|
||||
translateFn?: (s: string) => string;
|
||||
templates?: string | Document | Record<string, string>;
|
||||
getTemplate?: (s: string) => Element | Function | string | void;
|
||||
}
|
||||
|
||||
export class TemplateSet {
|
||||
@@ -51,6 +23,7 @@ export class TemplateSet {
|
||||
dev: boolean;
|
||||
rawTemplates: typeof globalTemplates = Object.create(globalTemplates);
|
||||
templates: { [name: string]: Template } = {};
|
||||
getRawTemplate?: (s: string) => Element | Function | string | void;
|
||||
translateFn?: (s: string) => string;
|
||||
translatableAttributes?: string[];
|
||||
Portal = Portal;
|
||||
@@ -68,6 +41,7 @@ export class TemplateSet {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.getRawTemplate = config.getTemplate;
|
||||
}
|
||||
|
||||
addTemplate(name: string, template: string | Element) {
|
||||
@@ -106,7 +80,7 @@ export class TemplateSet {
|
||||
|
||||
getTemplate(name: string): Template {
|
||||
if (!(name in this.templates)) {
|
||||
const rawTemplate = this.rawTemplates[name];
|
||||
const rawTemplate = this.getRawTemplate?.(name) || this.rawTemplates[name];
|
||||
if (rawTemplate === undefined) {
|
||||
let extraInfo = "";
|
||||
try {
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
// do not modify manually. This file is generated by the release script.
|
||||
export const version = "2.2.7";
|
||||
export const version = "2.2.9";
|
||||
|
||||
@@ -101,3 +101,55 @@ exports[`loading templates can load a few templates from an XMLDocument 2`] = `
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`loading templates getTemplate: element returned (2) 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div>Hello World!</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`loading templates getTemplate: element returned 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div>Hello World!</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`loading templates getTemplate: template string returned 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div>Hello World!</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`loading templates getTemplate: undefined returned 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div>Hello World!</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -78,4 +78,62 @@ describe("loading templates", () => {
|
||||
context.addTemplates(xml);
|
||||
expect(Object.keys(context.rawTemplates)).toEqual([]);
|
||||
});
|
||||
|
||||
test("getTemplate: element returned", () => {
|
||||
const context = new TestContext({
|
||||
getTemplate: (name) => {
|
||||
if (name === "main") {
|
||||
const data = `<div>Hello World!</div>`;
|
||||
const xml = new DOMParser().parseFromString(data, "text/xml");
|
||||
return xml.firstChild as Element;
|
||||
}
|
||||
return;
|
||||
},
|
||||
});
|
||||
const result = context.renderToString("main");
|
||||
expect(result).toBe("<div>Hello World!</div>");
|
||||
});
|
||||
|
||||
test("getTemplate: element returned (2)", () => {
|
||||
const context = new TestContext({
|
||||
getTemplate: (name) => {
|
||||
if (name === "main") {
|
||||
const doc = new Document();
|
||||
const div = doc.createElement("div");
|
||||
div.append(doc.createTextNode("Hello World!"));
|
||||
return div;
|
||||
}
|
||||
return;
|
||||
},
|
||||
});
|
||||
const result = context.renderToString("main");
|
||||
expect(result).toBe("<div>Hello World!</div>");
|
||||
});
|
||||
|
||||
test("getTemplate: template string returned", () => {
|
||||
const context = new TestContext({
|
||||
getTemplate: (name) => {
|
||||
if (name === "main") {
|
||||
return `<div>Hello World!</div>`;
|
||||
}
|
||||
return;
|
||||
},
|
||||
});
|
||||
const result = context.renderToString("main");
|
||||
expect(result).toBe("<div>Hello World!</div>");
|
||||
});
|
||||
|
||||
test("getTemplate: undefined returned", () => {
|
||||
const context = new TestContext({
|
||||
getTemplate: () => {},
|
||||
});
|
||||
const data = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<div t-name="main">Hello World!</div>
|
||||
</templates>`;
|
||||
const xml = new DOMParser().parseFromString(data, "text/xml");
|
||||
context.addTemplates(xml);
|
||||
const result = context.renderToString("main");
|
||||
expect(result).toBe("<div>Hello World!</div>");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -44,7 +44,26 @@ exports[`t-model directive .trim modifier 1`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
let { toNumber } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div><input block-property-0=\\"value\\" block-handler-1=\\"input\\"/><span><block-text-2/></span></div>\`);
|
||||
let block1 = createBlock(\`<div><input block-property-0=\\"value\\" block-handler-1=\\"change\\"/><span><block-text-2/></span></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const bExpr1 = ctx['state'];
|
||||
const expr1 = 'text';
|
||||
let prop1 = bExpr1[expr1];
|
||||
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value.trim(); }];
|
||||
let txt1 = ctx['state'].text;
|
||||
return block1([prop1, hdlr1, txt1]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-model directive .trim modifier implies .lazy modifier 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
let { toNumber } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div><input block-property-0=\\"value\\" block-handler-1=\\"change\\"/><span><block-text-2/></span></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const bExpr1 = ctx['state'];
|
||||
|
||||
@@ -331,6 +331,32 @@ describe("t-model directive", () => {
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||
});
|
||||
|
||||
test(".trim modifier implies .lazy modifier", async () => {
|
||||
class SomeComponent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<input t-model.trim="state.text"/>
|
||||
<span><t t-esc="state.text"/></span>
|
||||
</div>
|
||||
`;
|
||||
state = useState({ text: "" });
|
||||
}
|
||||
const comp = await mount(SomeComponent, fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><input><span></span></div>");
|
||||
|
||||
const input = fixture.querySelector("input")!;
|
||||
input.value = "test ";
|
||||
input.dispatchEvent(new Event("input"));
|
||||
await nextTick();
|
||||
expect(comp.state.text).toBe("");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span></span></div>");
|
||||
input.dispatchEvent(new Event("change"));
|
||||
await nextTick();
|
||||
expect(comp.state.text).toBe("test");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||
});
|
||||
|
||||
test(".number modifier", async () => {
|
||||
class SomeComponent extends Component {
|
||||
static template = xml`
|
||||
|
||||
Reference in New Issue
Block a user