From 942fcbfb3a56dcc26bfaa7cf049f266060a2df1e Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Tue, 10 Jan 2023 10:23:12 +0000 Subject: [PATCH] [FIX] *: views: add component instance to rendering context Owl last update fixes the leaking `this` in rendering context of templates called with `t-call-context` [1]. Since [2], we use the `t-call-context` directive to control the rendering context of templates compiled from an arch (e.g. in form and kanban views). We can now better control what people use in archs, where we don't want them to access js implementation details, as `this` is no longer available. However, the component instance still needs to be accessible in the compiled template. We thus add the `__comp__` key in the rendering context. Since we do not want people to access it in archs, we add a check in the view validation that this string isn't used in dynamic attributes. [1] https://github.com/odoo/owl/commit/df59ec49aefce2e0913fdc1792d42b9680fb28b6 [2] https://github.com/odoo/odoo/commit/4c5b867ff6b0b674cb83d1a1262ae354ebaa6d57 X-original-commit: 264f313012aa99449d1576ae1110252c13755142 Part-of: odoo/odoo#110196 --- .../src/views/activity/activity_record.js | 1 + .../src/views/activity/activity_record.xml | 2 +- .../static/src/views/form/form_compiler.js | 32 +++++++------- .../static/src/views/form/form_controller.xml | 2 +- .../form/project_sharing_form_compiler.js | 18 ++++---- .../form/project_sharing_form_controller.xml | 2 +- .../static/src/views/form/form_compiler.js | 43 +++++++++--------- .../static/src/views/form/form_controller.xml | 7 ++- .../static/src/views/form/form_renderer.js | 2 +- .../src/views/kanban/kanban_compiler.js | 11 +++-- .../static/src/views/kanban/kanban_record.js | 1 + .../static/src/views/kanban/kanban_record.xml | 2 +- addons/web/static/src/views/view_compiler.js | 16 ++++--- .../settings_form_compiler.js | 9 ++-- .../tests/views/form/form_compiler_tests.js | 44 +++++++++---------- .../settings_form_view_tests.js | 10 ++--- odoo/addons/base/models/ir_ui_view.py | 8 ++++ odoo/addons/base/tests/test_views.py | 36 ++++++++++++++- 18 files changed, 149 insertions(+), 97 deletions(-) diff --git a/addons/mail/static/src/views/activity/activity_record.js b/addons/mail/static/src/views/activity/activity_record.js index 2789ee0c416..9076bcefab4 100644 --- a/addons/mail/static/src/views/activity/activity_record.js +++ b/addons/mail/static/src/views/activity/activity_record.js @@ -45,6 +45,7 @@ export class ActivityRecord extends Component { user_context: this.user.context, widget: this.widget, luxon, + __comp__: Object.assign(Object.create(this), { this: this }), }; } } diff --git a/addons/mail/static/src/views/activity/activity_record.xml b/addons/mail/static/src/views/activity/activity_record.xml index 0b1ab6b4429..f0f6235af95 100644 --- a/addons/mail/static/src/views/activity/activity_record.xml +++ b/addons/mail/static/src/views/activity/activity_record.xml @@ -3,7 +3,7 @@ - + diff --git a/addons/mail/static/src/views/form/form_compiler.js b/addons/mail/static/src/views/form/form_compiler.js index 05c04b4b929..3b90df78eb7 100644 --- a/addons/mail/static/src/views/form/form_compiler.js +++ b/addons/mail/static/src/views/form/form_compiler.js @@ -86,7 +86,7 @@ export class MailFormCompiler extends ViewCompiler { const chatterContainerHookXml = res.querySelector(".o_FormRenderer_chatterContainer"); if (chatterContainerHookXml) { setAttributes(chatterContainerHookXml, { - "t-if": `!this.hasAttachmentViewer() and this.uiService.size >= ${SIZES.XXL}`, + "t-if": `!__comp__.hasAttachmentViewer() and __comp__.uiService.size >= ${SIZES.XXL}`, "t-attf-class": "o-aside", }); const chatterContainerXml = chatterContainerHookXml.querySelector("ChatterContainer"); @@ -99,7 +99,7 @@ export class MailFormCompiler extends ViewCompiler { const attachmentViewHookXml = res.querySelector(".o_attachment_preview"); if (attachmentViewHookXml) { setAttributes(attachmentViewHookXml, { - "t-if": `this.hasAttachmentViewer()`, + "t-if": `__comp__.hasAttachmentViewer()`, }); } return res; @@ -112,7 +112,7 @@ export class MailFormCompiler extends ViewCompiler { let compiledChild = this.compileNode(child, params, false); compiledChild = this.applyInvisible(invisible, compiledChild, { ...params, - recordExpr: "this.model.root", + recordExpr: "__comp__.model.root", }); append(compiledRoot, compiledChild); } @@ -122,16 +122,16 @@ export class MailFormCompiler extends ViewCompiler { compileChatter(node) { return compileChatter(node, { chatter: "chatter", - threadId: "this.model.root.resId or undefined", - threadModel: "this.model.root.resModel", - webRecord: "this.model.root", + threadId: "__comp__.model.root.resId or undefined", + threadModel: "__comp__.model.root.resModel", + webRecord: "__comp__.model.root", }); } compileAttachmentPreview(node) { return compileAttachmentPreview(node, { - threadId: "this.model.root.resId or undefined", - threadModel: "this.model.root.resModel", + threadId: "__comp__.model.root.resId or undefined", + threadModel: "__comp__.model.root.resModel", }); } } @@ -140,10 +140,10 @@ registry.category("form_compilers").add("chatter_compiler", { selector: "div.oe_chatter", fn: (node) => compileChatter(node, { - chatter: "this.props.chatter", - threadId: "this.props.record.resId or undefined", - threadModel: "this.props.record.resModel", - webRecord: "this.props.record", + chatter: "__comp__.props.chatter", + threadId: "__comp__.props.record.resId or undefined", + threadModel: "__comp__.props.record.resModel", + webRecord: "__comp__.props.record", }), }); @@ -151,8 +151,8 @@ registry.category("form_compilers").add("attachment_preview_compiler", { selector: "div.o_attachment_preview", fn: (node) => compileAttachmentPreview(node, { - threadId: "this.props.record.resId or undefined", - threadModel: "this.props.record.resModel", + threadId: "__comp__.props.record.resId or undefined", + threadModel: "__comp__.props.record.resModel", }), }); @@ -183,7 +183,7 @@ patch(FormCompiler.prototype, "mail", { const sheetBgChatterContainerHookXml = chatterContainerHookXml.cloneNode(true); sheetBgChatterContainerHookXml.classList.add("o-isInFormSheetBg"); setAttributes(sheetBgChatterContainerHookXml, { - "t-if": `this.props.hasAttachmentViewer`, + "t-if": `__comp__.props.hasAttachmentViewer`, }); append(formSheetBgXml, sheetBgChatterContainerHookXml); const sheetBgChatterContainerXml = sheetBgChatterContainerHookXml.querySelector( @@ -195,7 +195,7 @@ patch(FormCompiler.prototype, "mail", { } // after sheet bg (standard position, below form) setAttributes(chatterContainerHookXml, { - "t-if": `!this.props.hasAttachmentViewer and this.uiService.size < ${SIZES.XXL}`, + "t-if": `!__comp__.props.hasAttachmentViewer and __comp__.uiService.size < ${SIZES.XXL}`, }); append(parentXml, chatterContainerHookXml); return res; diff --git a/addons/mail/static/src/views/form/form_controller.xml b/addons/mail/static/src/views/form/form_controller.xml index c56f5653f92..76d273d7d81 100644 --- a/addons/mail/static/src/views/form/form_controller.xml +++ b/addons/mail/static/src/views/form/form_controller.xml @@ -4,7 +4,7 @@ - + diff --git a/addons/project/static/src/project_sharing/views/form/project_sharing_form_compiler.js b/addons/project/static/src/project_sharing/views/form/project_sharing_form_compiler.js index 07ab607630f..f889a352bc3 100644 --- a/addons/project/static/src/project_sharing/views/form/project_sharing_form_compiler.js +++ b/addons/project/static/src/project_sharing/views/form/project_sharing_form_compiler.js @@ -40,7 +40,7 @@ export class ProjectSharingChatterCompiler extends ViewCompiler { const chatterContainerHookXml = res.querySelector(".o_FormRenderer_chatterContainer"); if (chatterContainerHookXml) { setAttributes(chatterContainerHookXml, { - "t-if": `this.uiService.size >= ${SIZES.XXL}`, + "t-if": `__comp__.uiService.size >= ${SIZES.XXL}`, }); chatterContainerHookXml.classList.add('overflow-x-hidden', 'overflow-y-auto', 'o-aside', 'h-100'); } @@ -54,7 +54,7 @@ export class ProjectSharingChatterCompiler extends ViewCompiler { let compiledChild = this.compileNode(child, params, false); compiledChild = this.applyInvisible(invisible, compiledChild, { ...params, - recordExpr: "this.model.root", + recordExpr: "__comp__.model.root", }); append(compiledRoot, compiledChild); } @@ -63,9 +63,9 @@ export class ProjectSharingChatterCompiler extends ViewCompiler { compileChatter(node) { return compileChatter(node, { - resId: 'this.model.root.resId or undefined', - resModel: 'this.model.root.resModel', - projectSharingId: 'this.model.root.context.active_id', + resId: '__comp__.model.root.resId or undefined', + resModel: '__comp__.model.root.resModel', + projectSharingId: '__comp__.model.root.context.active_id', }); } } @@ -74,9 +74,9 @@ registry.category("form_compilers").add("portal_chatter_compiler", { selector: "div.oe_chatter", fn: (node) => compileChatter(node, { - resId: "this.props.record.resId or undefined", - resModel: "this.props.record.resModel", - projectSharingId: "this.props.record.context.active_id", + resId: "__comp__.props.record.resId or undefined", + resModel: "__comp__.props.record.resModel", + projectSharingId: "__comp__.props.record.context.active_id", }), }); @@ -97,7 +97,7 @@ patch(FormCompiler.prototype, 'project_sharing_chatter', { } // after sheet bg (standard position, below form) setAttributes(chatterContainerHookXml, { - 't-if': `this.uiService.size < ${SIZES.XXL}`, + 't-if': `__comp__.uiService.size < ${SIZES.XXL}`, }); append(parentXml, chatterContainerHookXml); return res; diff --git a/addons/project/static/src/project_sharing/views/form/project_sharing_form_controller.xml b/addons/project/static/src/project_sharing/views/form/project_sharing_form_controller.xml index 6db6caaad52..fd8d2ad5804 100644 --- a/addons/project/static/src/project_sharing/views/form/project_sharing_form_controller.xml +++ b/addons/project/static/src/project_sharing/views/form/project_sharing_form_controller.xml @@ -4,7 +4,7 @@ - + diff --git a/addons/web/static/src/views/form/form_compiler.js b/addons/web/static/src/views/form/form_compiler.js index 59d4d35d31c..46956fed031 100644 --- a/addons/web/static/src/views/form/form_compiler.js +++ b/addons/web/static/src/views/form/form_compiler.js @@ -76,13 +76,13 @@ export class FormCompiler extends ViewCompiler { } else { labelText = labelText ? toStringExpression(labelText) - : `this.props.record.fields['${fieldName}'].string`; + : `__comp__.props.record.fields['${fieldName}'].string`; } const formLabel = createElement("FormLabel", { id: `'${fieldId}'`, fieldName: `'${fieldName}'`, - record: `this.props.record`, - fieldInfo: `this.props.archInfo.fieldNodes['${fieldId}']`, + record: `__comp__.props.record`, + fieldInfo: `__comp__.props.archInfo.fieldNodes['${fieldId}']`, className: `"${label.className}"`, string: labelText, }); @@ -140,7 +140,7 @@ export class FormCompiler extends ViewCompiler { if (typeof invisible === "boolean") { isVisibleExpr = `${invisible ? false : true}`; } else { - isVisibleExpr = `!this.evalDomainFromRecord(this.props.record,${JSON.stringify( + isVisibleExpr = `!__comp__.evalDomainFromRecord(__comp__.props.record,${JSON.stringify( invisible )})`; } @@ -176,8 +176,8 @@ export class FormCompiler extends ViewCompiler { compileButton(el, params) { const compiled = super.compileButton(el, params); - compiled.setAttribute("disable", "this.props.disableViewButtons"); - compiled.setAttribute("enable", "this.props.enableViewButtons"); + compiled.setAttribute("disable", "__comp__.props.disableViewButtons"); + compiled.setAttribute("enable", "__comp__.props.enableViewButtons"); return compiled; } @@ -219,13 +219,13 @@ export class FormCompiler extends ViewCompiler { compileForm(el, params) { const sheetNode = el.querySelector("sheet"); const displayClasses = sheetNode - ? `d-flex {{ this.uiService.size < ${SIZES.XXL} ? "flex-column" : "flex-nowrap h-100" }}` + ? `d-flex {{ __comp__.uiService.size < ${SIZES.XXL} ? "flex-column" : "flex-nowrap h-100" }}` : "d-block"; const stateClasses = - "{{ this.props.record.isDirty ? 'o_form_dirty' : !this.props.record.isVirtual ? 'o_form_saved' : '' }}"; + "{{ __comp__.props.record.isDirty ? 'o_form_dirty' : !__comp__.props.record.isVirtual ? 'o_form_saved' : '' }}"; const form = createElement("div", { - "t-att-class": "this.props.class", - "t-attf-class": `{{this.props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} ${displayClasses} ${stateClasses}`, + "t-att-class": "__comp__.props.class", + "t-attf-class": `{{__comp__.props.record.isInEdition ? 'o_form_editable' : 'o_form_readonly'}} ${displayClasses} ${stateClasses}`, }); if (!sheetNode) { for (const child of el.childNodes) { @@ -249,8 +249,9 @@ export class FormCompiler extends ViewCompiler { if (localization.multiLang) { const statusBar = form.querySelector(".o_form_statusbar"); const translateAlert = createElement("t", { - "t-if": "this.props.translateAlert", + "t-if": "__comp__.props.translateAlert", "t-call": "web.TranslateAlert", + "t-call-context": "__comp__", }); if (statusBar) { statusBar.parentElement.insertBefore(translateAlert, statusBar.nextSibling); @@ -332,14 +333,14 @@ export class FormCompiler extends ViewCompiler { const props = { id: `${fieldId}`, fieldName: `'${fieldName}'`, - record: `this.props.record`, + record: `__comp__.props.record`, string: child.hasAttribute("string") ? toStringExpression(child.getAttribute("string")) - : `this.props.record.fields.${fieldName}.string`, - fieldInfo: `this.props.archInfo.fieldNodes[${fieldId}]`, + : `__comp__.props.record.fields.${fieldName}.string`, + fieldInfo: `__comp__.props.archInfo.fieldNodes[${fieldId}]`, }; mainSlot.setAttribute("props", objectToString(props)); - mainSlot.setAttribute("Component", "this.constructor.components.FormLabel"); + mainSlot.setAttribute("Component", "__comp__.constructor.components.FormLabel"); mainSlot.setAttribute("subType", "'item_component'"); } } else { @@ -360,7 +361,7 @@ export class FormCompiler extends ViewCompiler { if (typeof invisible === "boolean") { isVisibleExpr = `${invisible ? false : true}`; } else { - isVisibleExpr = `!this.evalDomainFromRecord(this.props.record,${JSON.stringify( + isVisibleExpr = `!__comp__.evalDomainFromRecord(__comp__.props.record,${JSON.stringify( invisible )})`; } @@ -504,11 +505,11 @@ export class FormCompiler extends ViewCompiler { noteBook.setAttribute( "defaultPage", - `this.props.record.isNew ? undefined : this.props.activeNotebookPages[${noteBookId}]` + `__comp__.props.record.isNew ? undefined : __comp__.props.activeNotebookPages[${noteBookId}]` ); noteBook.setAttribute( "onPageUpdate", - `(page) => this.props.onNotebookPageChange(${noteBookId}, page)` + `(page) => __comp__.props.onNotebookPageChange(${noteBookId}, page)` ); for (const child of el.children) { @@ -539,7 +540,7 @@ export class FormCompiler extends ViewCompiler { if (child.getAttribute("autofocus") === "autofocus") { noteBook.setAttribute( "defaultPage", - `this.props.record.isNew ? "${pageId}" : (this.props.activeNotebookPages[${noteBookId}] || "${pageId}")` + `__comp__.props.record.isNew ? "${pageId}" : (__comp__.props.activeNotebookPages[${noteBookId}] || "${pageId}")` ); } @@ -558,7 +559,7 @@ export class FormCompiler extends ViewCompiler { if (typeof invisible === "boolean") { isVisible = `${!invisible}`; } else { - isVisible = `!this.evalDomainFromRecord(this.props.record,${JSON.stringify( + isVisible = `!__comp__.evalDomainFromRecord(__comp__.props.record,${JSON.stringify( invisible )})`; } @@ -632,7 +633,7 @@ export class FormCompiler extends ViewCompiler { */ compileWidget(el) { const widget = super.compileWidget(el); - widget.setAttribute("readonly", `!this.props.record.isInEdition`); + widget.setAttribute("readonly", `!__comp__.props.record.isInEdition`); return widget; } } diff --git a/addons/web/static/src/views/form/form_controller.xml b/addons/web/static/src/views/form/form_controller.xml index 69e8bde22af..2289433124b 100644 --- a/addons/web/static/src/views/form/form_controller.xml +++ b/addons/web/static/src/views/form/form_controller.xml @@ -66,16 +66,15 @@
Please update translations of : - + , - +
diff --git a/addons/web/static/src/views/form/form_renderer.js b/addons/web/static/src/views/form/form_renderer.js index 2035344a22b..468a6eb163e 100644 --- a/addons/web/static/src/views/form/form_renderer.js +++ b/addons/web/static/src/views/form/form_renderer.js @@ -93,7 +93,7 @@ export class FormRenderer extends Component { } } -FormRenderer.template = xml``; +FormRenderer.template = xml``; FormRenderer.components = { Field, FormLabel, diff --git a/addons/web/static/src/views/kanban/kanban_compiler.js b/addons/web/static/src/views/kanban/kanban_compiler.js index 0cbf293fb04..634e0892d20 100644 --- a/addons/web/static/src/views/kanban/kanban_compiler.js +++ b/addons/web/static/src/views/kanban/kanban_compiler.js @@ -80,7 +80,7 @@ export class KanbanCompiler extends ViewCompiler { const strParams = Object.entries(nodeParams) .map(([k, v]) => [k, toStringExpression(v)].join(":")) .join(","); - el.setAttribute("t-on-click", `()=>this.triggerAction({${strParams}})`); + el.setAttribute("t-on-click", `()=>__comp__.triggerAction({${strParams}})`); const compiled = createElement(el.nodeName); for (const { name, value } of el.attributes) { @@ -100,7 +100,10 @@ export class KanbanCompiler extends ViewCompiler { * @returns {Element} */ compileColorPicker() { - return createElement("t", { "t-call": "web.KanbanColorPicker" }); + return createElement("t", { + "t-call": "web.KanbanColorPicker", + "t-call-context": "__comp__", + }); } /** @@ -117,7 +120,7 @@ export class KanbanCompiler extends ViewCompiler { } else { compiled = super.compileField(el, params); const fieldId = el.getAttribute("field_id") || el.getAttribute("name"); - compiled.setAttribute("id", `'${fieldId}_' + this.props.record.id`); + compiled.setAttribute("id", `'${fieldId}_' + __comp__.props.record.id`); } const { bold, display } = extractAttributes(el, ["bold", "display"]); @@ -177,7 +180,7 @@ export class KanbanCompiler extends ViewCompiler { const compiled = this.compileGenericNode(el, params); const tname = el.getAttribute("t-call"); if (tname in this.templates) { - compiled.setAttribute("t-call", `{{this.templates[${toStringExpression(tname)}]}}`); + compiled.setAttribute("t-call", `{{__comp__.templates[${toStringExpression(tname)}]}}`); } return compiled; } diff --git a/addons/web/static/src/views/kanban/kanban_record.js b/addons/web/static/src/views/kanban/kanban_record.js index 165890e73a0..7d85001f18e 100644 --- a/addons/web/static/src/views/kanban/kanban_record.js +++ b/addons/web/static/src/views/kanban/kanban_record.js @@ -304,6 +304,7 @@ export class KanbanRecord extends Component { selection_mode: this.props.forceGlobalClick, user_context: this.user.context, widget: this.widget, + __comp__: Object.assign(Object.create(this), { this: this }), }; } } diff --git a/addons/web/static/src/views/kanban/kanban_record.xml b/addons/web/static/src/views/kanban/kanban_record.xml index 6ff60900c1e..0f3906b4a0d 100644 --- a/addons/web/static/src/views/kanban/kanban_record.xml +++ b/addons/web/static/src/views/kanban/kanban_record.xml @@ -9,7 +9,7 @@ t-att-tabindex="props.record.model.useSampleModel ? -1 : 0" t-on-click="onGlobalClick" t-ref="root"> - + diff --git a/addons/web/static/src/views/view_compiler.js b/addons/web/static/src/views/view_compiler.js index 2d7f4a12741..17ad76fdcf4 100644 --- a/addons/web/static/src/views/view_compiler.js +++ b/addons/web/static/src/views/view_compiler.js @@ -223,7 +223,7 @@ export class ViewCompiler { { selector: "widget", fn: this.compileWidget }, ]; this.templates = templates; - this.ctx = { readonly: "this.props.readonly" }; + this.ctx = { readonly: "__comp__.props.readonly" }; this.owlDirectiveRegexesWhitelist = this.constructor.OWL_DIRECTIVE_WHITELIST.map( (d) => new RegExp(d) @@ -246,8 +246,10 @@ export class ViewCompiler { if (typeof invisible === "boolean") { return; } - const recordExpr = params.recordExpr || "this.props.record"; - let isVisileExpr = `!this.evalDomainFromRecord(${recordExpr},${JSON.stringify(invisible)})`; + const recordExpr = params.recordExpr || "__comp__.props.record"; + let isVisileExpr = `!__comp__.evalDomainFromRecord(${recordExpr},${JSON.stringify( + invisible + )})`; if (compiled.hasAttribute("t-if")) { const formerTif = compiled.getAttribute("t-if"); isVisileExpr = `( ${formerTif} ) and ${isVisileExpr}`; @@ -323,7 +325,7 @@ export class ViewCompiler { } const button = createElement("ViewButton", { tag: toStringExpression(tag), - record: "this.props.record", + record: "__comp__.props.record", }); assignOwlDirectives(button, el); @@ -382,8 +384,8 @@ export class ViewCompiler { const field = createElement("Field"); field.setAttribute("id", `'${fieldId}'`); field.setAttribute("name", `'${fieldName}'`); - field.setAttribute("record", params.recordExpr || "this.props.record"); - field.setAttribute("fieldInfo", `this.props.archInfo.fieldNodes['${fieldId}']`); + field.setAttribute("record", params.recordExpr || "__comp__.props.record"); + field.setAttribute("fieldInfo", `__comp__.props.archInfo.fieldNodes['${fieldId}']`); if (el.hasAttribute("widget")) { field.setAttribute("type", `'${el.getAttribute("widget")}'`); @@ -422,7 +424,7 @@ export class ViewCompiler { */ compileWidget(el) { const attrs = {}; - const props = { record: "this.props.record", readonly: this.ctx.readonly }; + const props = { record: "__comp__.props.record", readonly: this.ctx.readonly }; for (const { name, value } of el.attributes) { switch (name) { case "class": diff --git a/addons/web/static/src/webclient/settings_form_view/settings_form_compiler.js b/addons/web/static/src/webclient/settings_form_view/settings_form_compiler.js index 88f604f3a25..082e2de82e4 100644 --- a/addons/web/static/src/webclient/settings_form_view/settings_form_compiler.js +++ b/addons/web/static/src/webclient/settings_form_view/settings_form_compiler.js @@ -17,8 +17,11 @@ export class SettingsFormCompiler extends FormCompiler { compileForm(el, params) { const settingsPage = createElement("SettingsPage"); - settingsPage.setAttribute("slots", "{NoContentHelper:this.props.slots.NoContentHelper}"); - settingsPage.setAttribute("initialTab", "this.props.initialApp"); + settingsPage.setAttribute( + "slots", + "{NoContentHelper:__comp__.props.slots.NoContentHelper}" + ); + settingsPage.setAttribute("initialTab", "__comp__.props.initialApp"); settingsPage.setAttribute("t-slot-scope", "settings"); //props @@ -91,7 +94,7 @@ export class SettingsFormCompiler extends FormCompiler { help: toStringExpression(el.getAttribute("help") || ""), companyDependent: el.getAttribute("company_dependent") === "1" || "false", documentation: toStringExpression(el.getAttribute("documentation") || ""), - record: `this.props.record`, + record: `__comp__.props.record`, }); let string = toStringExpression(el.getAttribute("string") || ""); let addLabel = true; diff --git a/addons/web/static/tests/views/form/form_compiler_tests.js b/addons/web/static/tests/views/form/form_compiler_tests.js index 3b0c4f3f6c7..85382ee55ed 100644 --- a/addons/web/static/tests/views/form/form_compiler_tests.js +++ b/addons/web/static/tests/views/form/form_compiler_tests.js @@ -38,7 +38,7 @@ QUnit.module("Form Compiler", (hooks) => { const arch = /*xml*/ `
lol
`; const expected = /*xml*/ ` -
+
lol
`; @@ -52,9 +52,9 @@ QUnit.module("Form Compiler", (hooks) => { const arch = /*xml*/ `