mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
@@ -104,6 +104,17 @@ root widget element.
|
||||
</div>
|
||||
```
|
||||
|
||||
Warning: there is a small caveat with dynamic class attributes: since Owl needs
|
||||
to be able to add/remove proper classes whenever necessary, it needs to be aware
|
||||
of the possible classes. Otherwise, it will not be able to make the difference
|
||||
between a valid css class added by the component, or some custom code, and a
|
||||
class that need to be removed. This is why we only support the explicit syntax
|
||||
with a class object:
|
||||
|
||||
```js
|
||||
<t t-widget="MyWidget" t-att-class="{a: state.flagA, b: state.flagB}">
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Reference
|
||||
|
||||
+31
-8
@@ -1211,20 +1211,41 @@ const widgetDirective: Directive = {
|
||||
if (ref) {
|
||||
finalizeWidgetCode += `;delete context.refs[${refKey}]`;
|
||||
}
|
||||
ctx.addIf(`isNew${widgetID}`);
|
||||
let createHook = "";
|
||||
let classAttr = node.getAttribute("class");
|
||||
let tattClass = node.getAttribute("t-att-class");
|
||||
let styleAttr = node.getAttribute("style");
|
||||
if (classAttr || styleAttr) {
|
||||
const classCode = classAttr
|
||||
? classAttr
|
||||
let tattStyle = node.getAttribute("t-att-style");
|
||||
if (tattStyle) {
|
||||
const attVar = `_${ctx.generateID()}`;
|
||||
ctx.addLine(`const ${attVar} = ${ctx.formatExpression(tattStyle)};`);
|
||||
tattStyle = attVar;
|
||||
}
|
||||
let updateClassCode = "";
|
||||
if (classAttr || tattClass || styleAttr || tattStyle) {
|
||||
let classCode = "";
|
||||
if (classAttr) {
|
||||
classCode =
|
||||
classAttr
|
||||
.split(" ")
|
||||
.map(c => `vn.elm.classList.add('${c}')`)
|
||||
.join(";") + ";"
|
||||
: "";
|
||||
const styleCode = styleAttr ? `vn.elm.style = '${styleAttr}'` : "";
|
||||
.join(";") + ";";
|
||||
}
|
||||
if (tattClass) {
|
||||
const attVar = `_${ctx.generateID()}`;
|
||||
ctx.addLine(`const ${attVar} = ${ctx.formatExpression(tattClass)};`);
|
||||
classCode = `for (let k in ${attVar}) {
|
||||
if (${attVar}[k]) {
|
||||
vn.elm.classList.add(k);
|
||||
}
|
||||
}`;
|
||||
updateClassCode = `let cl=w${widgetID}.el.classList;for (let k in ${attVar}) {if (${attVar}[k]) {cl.add(k)} else {cl.remove(k)}}`;
|
||||
}
|
||||
const styleExpr = tattStyle || (styleAttr ? `'${styleAttr}'` : false);
|
||||
const styleCode = styleExpr ? `vn.elm.style = ${styleExpr}` : "";
|
||||
createHook = `vnode.data.hook = {create(_, vn){${classCode}${styleCode}}};`;
|
||||
}
|
||||
ctx.addIf(`isNew${widgetID}`);
|
||||
ctx.addLine(
|
||||
`def${defID} = def${defID}.then(vnode=>{${createHook}let pvnode=h(vnode.sel, {key: ${templateID}});c${
|
||||
ctx.parentNode
|
||||
@@ -1232,7 +1253,9 @@ const widgetDirective: Directive = {
|
||||
);
|
||||
ctx.addElse();
|
||||
ctx.addLine(
|
||||
`def${defID} = def${defID}.then(()=>{if (w${widgetID}.__owl__.isDestroyed) {return};let vnode;if (!w${widgetID}.__owl__.vnode){vnode=w${widgetID}.__owl__.pvnode} else { vnode=h(w${widgetID}.__owl__.vnode.sel, {key: ${templateID}});vnode.elm=w${widgetID}.el;vnode.data.hook = {insert(a){a.elm.parentNode.replaceChild(w${widgetID}.el,a.elm);a.elm=w${widgetID}.el;w${widgetID}.__mount();},remove(){${finalizeWidgetCode}}, destroy() {${finalizeWidgetCode}}}}c${
|
||||
`def${defID} = def${defID}.then(()=>{if (w${widgetID}.__owl__.isDestroyed) {return};${
|
||||
tattStyle ? `w${widgetID}.el.style=${tattStyle};` : ""
|
||||
}${updateClassCode}let vnode;if (!w${widgetID}.__owl__.vnode){vnode=w${widgetID}.__owl__.pvnode} else { vnode=h(w${widgetID}.__owl__.vnode.sel, {key: ${templateID}});vnode.elm=w${widgetID}.el;vnode.data.hook = {insert(a){a.elm.parentNode.replaceChild(w${widgetID}.el,a.elm);a.elm=w${widgetID}.el;w${widgetID}.__mount();},remove(){${finalizeWidgetCode}}, destroy() {${finalizeWidgetCode}}}}c${
|
||||
ctx.parentNode
|
||||
}[_${dummyID}_index]=vnode;});`
|
||||
);
|
||||
|
||||
@@ -1,5 +1,103 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`class and style attributes with t-widget dynamic t-att-style is properly added and updated on widget root el 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//WIDGET
|
||||
let _2_index = c1.length;
|
||||
c1.push(null);
|
||||
let def3;
|
||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
||||
let props4 = {};
|
||||
let isNew4 = !w4;
|
||||
if (w4 && w4.__owl__.renderPromise) {
|
||||
if (w4.__owl__.isStarted) {
|
||||
def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||
} else {
|
||||
isNew4 = true
|
||||
if (props4 === w4.__owl__.renderProps) {
|
||||
def3 = w4.__owl__.renderPromise;
|
||||
} else {
|
||||
w4.destroy();
|
||||
w4 = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!def3) {
|
||||
if (w4) {
|
||||
def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||
} else {
|
||||
w4 = new context.widgets['child'](owner, props4);
|
||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
||||
def3 = w4._prepare();
|
||||
}
|
||||
}
|
||||
const _5 = context['state'].style;
|
||||
if (isNew4) {
|
||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.style = _5}};let pvnode=h(vnode.sel, {key: 4});c1[_2_index]=pvnode;pvnode.data.hook = {insert(vn){let nvn=w4._mount(vnode, vn.elm);pvnode.elm=nvn.elm;},remove(){w4.destroy()},destroy(){w4.destroy()}}; w4.__owl__.pvnode = pvnode;});
|
||||
} else {
|
||||
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};w4.el.style=_5;let vnode;if (!w4.__owl__.vnode){vnode=w4.__owl__.pvnode} else { vnode=h(w4.__owl__.vnode.sel, {key: 4});vnode.elm=w4.el;vnode.data.hook = {insert(a){a.elm.parentNode.replaceChild(w4.el,a.elm);a.elm=w4.el;w4.__mount();},remove(){w4.destroy()}, destroy() {w4.destroy()}}}c1[_2_index]=vnode;});
|
||||
}
|
||||
extra.promises.push(def3);
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`class and style attributes with t-widget t-att-class is properly added/removed on widget root el 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//WIDGET
|
||||
let _2_index = c1.length;
|
||||
c1.push(null);
|
||||
let def3;
|
||||
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
|
||||
let props4 = {};
|
||||
let isNew4 = !w4;
|
||||
if (w4 && w4.__owl__.renderPromise) {
|
||||
if (w4.__owl__.isStarted) {
|
||||
def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||
} else {
|
||||
isNew4 = true
|
||||
if (props4 === w4.__owl__.renderProps) {
|
||||
def3 = w4.__owl__.renderPromise;
|
||||
} else {
|
||||
w4.destroy();
|
||||
w4 = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!def3) {
|
||||
if (w4) {
|
||||
def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||
} else {
|
||||
w4 = new context.widgets['child'](owner, props4);
|
||||
context.__owl__.cmap[4] = w4.__owl__.id;
|
||||
def3 = w4._prepare();
|
||||
}
|
||||
}
|
||||
const _5 = {a: context['state'].a,b: context['state'].b};
|
||||
if (isNew4) {
|
||||
def3 = def3.then(vnode=>{vnode.data.hook = {create(_, vn){for (let k in _5) {
|
||||
if (_5[k]) {
|
||||
vn.elm.classList.add(k);
|
||||
}
|
||||
}}};let pvnode=h(vnode.sel, {key: 4});c1[_2_index]=pvnode;pvnode.data.hook = {insert(vn){let nvn=w4._mount(vnode, vn.elm);pvnode.elm=nvn.elm;},remove(){w4.destroy()},destroy(){w4.destroy()}}; w4.__owl__.pvnode = pvnode;});
|
||||
} else {
|
||||
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let cl=w4.el.classList;for (let k in _5) {if (_5[k]) {cl.add(k)} else {cl.remove(k)}}let vnode;if (!w4.__owl__.vnode){vnode=w4.__owl__.pvnode} else { vnode=h(w4.__owl__.vnode.sel, {key: 4});vnode.elm=w4.el;vnode.data.hook = {insert(a){a.elm.parentNode.replaceChild(w4.el,a.elm);a.elm=w4.el;w4.__mount();},remove(){w4.destroy()}, destroy() {w4.destroy()}}}c1[_2_index]=vnode;});
|
||||
}
|
||||
extra.promises.push(def3);
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`composition sub widgets with some state rendered in a loop 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
|
||||
+60
-2
@@ -1234,13 +1234,39 @@ describe("class and style attributes with t-widget", () => {
|
||||
widgets = { child: Child };
|
||||
}
|
||||
class Child extends Widget {
|
||||
inlineTemplate = `<div class="c"/>`
|
||||
inlineTemplate = `<div class="c"/>`;
|
||||
}
|
||||
const widget = new ParentWidget(env);
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div><div class="c a b"></div></div>`);
|
||||
});
|
||||
|
||||
test("t-att-class is properly added/removed on widget root el", async () => {
|
||||
env.qweb.addTemplate(
|
||||
"parent",
|
||||
`<div>
|
||||
<t t-widget="child" t-att-class="{a:state.a, b:state.b}"/>
|
||||
</div>`
|
||||
);
|
||||
class ParentWidget extends Widget {
|
||||
template = "parent";
|
||||
widgets = { child: Child };
|
||||
state = { a: true, b: false };
|
||||
}
|
||||
class Child extends Widget {
|
||||
inlineTemplate = `<div class="c"/>`;
|
||||
}
|
||||
const widget = new ParentWidget(env);
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div><div class="c a"></div></div>`);
|
||||
expect(env.qweb.templates.parent.fn.toString()).toMatchSnapshot();
|
||||
|
||||
widget.state.a = false;
|
||||
widget.state.b = true;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(`<div><div class="c b"></div></div>`);
|
||||
});
|
||||
|
||||
test("style is properly added on widget root el", async () => {
|
||||
class ParentWidget extends Widget {
|
||||
inlineTemplate = `
|
||||
@@ -1251,7 +1277,39 @@ describe("class and style attributes with t-widget", () => {
|
||||
}
|
||||
const widget = new ParentWidget(env);
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div><div style="font-weight: bold;"></div></div>`);
|
||||
expect(fixture.innerHTML).toBe(
|
||||
`<div><div style="font-weight: bold;"></div></div>`
|
||||
);
|
||||
});
|
||||
|
||||
test("dynamic t-att-style is properly added and updated on widget root el", async () => {
|
||||
env.qweb.addTemplate(
|
||||
"parent",
|
||||
`
|
||||
<div>
|
||||
<t t-widget="child" t-att-style="state.style"/>
|
||||
</div>`
|
||||
);
|
||||
class ParentWidget extends Widget {
|
||||
template = "parent";
|
||||
widgets = { child: Widget };
|
||||
state = { style: "font-size: 20px" };
|
||||
}
|
||||
const widget = new ParentWidget(env);
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(env.qweb.templates.parent.fn.toString()).toMatchSnapshot();
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
`<div><div style="font-size: 20px;"></div></div>`
|
||||
);
|
||||
|
||||
widget.state.style = "font-size: 30px";
|
||||
await nextTick();
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
`<div><div style="font-size: 30px;"></div></div>`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user