mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] qweb/component: refactoring of scoping/variables/slots
This commit is a significant refactoring of the internal of QWeb. It simplifies the way variables/scoping and slots interact together. The main idea is that we use a simple scope object instead of a context/var/scope object. This commit also implement the actual correct QWeb semantic for the t-call directive with a sub body. Before, we simply extracted the variables from the body and injected them at the top of the sub template. We now simply compile the body before the sub template. This is a joint work with Lucas (lpe). closes #541 closes #544 closes #545 closes #557 closes #556
This commit is contained in:
@@ -70,10 +70,9 @@ interface Internal<T extends Env, Props> {
|
||||
parentLastFiberId: number;
|
||||
|
||||
// when a rendering is initiated by a parent, it may set variables in 'scope'
|
||||
// and 'vars' (typically when the component is rendered in a slot). We need to
|
||||
// (typically when the component is rendered in a slot). We need to
|
||||
// store that information in case the component would be re-rendered later on.
|
||||
scope: any;
|
||||
vars: any;
|
||||
|
||||
boundHandlers: { [key: number]: any };
|
||||
observer: Observer | null;
|
||||
@@ -198,8 +197,7 @@ export class Component<T extends Env, Props extends {}> {
|
||||
renderFn: qweb.render.bind(qweb, template),
|
||||
classObj: null,
|
||||
refs: null,
|
||||
scope: null,
|
||||
vars: null
|
||||
scope: null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -510,9 +508,8 @@ export class Component<T extends Env, Props extends {}> {
|
||||
* The __updateProps method is called by the t-component directive whenever
|
||||
* it updates a component (so, when the parent template is rerendered).
|
||||
*/
|
||||
async __updateProps(nextProps: Props, parentFiber: Fiber, scope: any, vars: any): Promise<void> {
|
||||
async __updateProps(nextProps: Props, parentFiber: Fiber, scope: any): Promise<void> {
|
||||
this.__owl__.scope = scope;
|
||||
this.__owl__.vars = vars;
|
||||
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
|
||||
if (shouldUpdate) {
|
||||
const __owl__ = this.__owl__;
|
||||
@@ -566,12 +563,11 @@ export class Component<T extends Env, Props extends {}> {
|
||||
|
||||
/**
|
||||
* The __prepare method is only called by the t-component directive, when a
|
||||
* subcomponent is created. It gets its scope and vars, if any, from the
|
||||
* subcomponent is created. It gets its scope, if any, from the
|
||||
* parent template.
|
||||
*/
|
||||
__prepare(parentFiber: Fiber, scope: any, vars: any, cb: CallableFunction): Fiber {
|
||||
__prepare(parentFiber: Fiber, scope: any, cb: CallableFunction): Fiber {
|
||||
this.__owl__.scope = scope;
|
||||
this.__owl__.vars = vars;
|
||||
const fiber = new Fiber(parentFiber, this, parentFiber.force, null);
|
||||
fiber.shouldPatch = false;
|
||||
if (!parentFiber.child) {
|
||||
|
||||
+10
-27
@@ -190,10 +190,10 @@ QWeb.addDirective({
|
||||
priority: 100,
|
||||
atNodeEncounter({ ctx, value, node, qweb }): boolean {
|
||||
ctx.addLine("//COMPONENT");
|
||||
ctx.rootContext.shouldDefineOwner = true;
|
||||
ctx.rootContext.shouldDefineQWeb = true;
|
||||
ctx.rootContext.shouldDefineParent = true;
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
ctx.rootContext.shouldDefineScope = true;
|
||||
let hasDynamicProps = node.getAttribute("t-props") ? true : false;
|
||||
|
||||
// t-on- events and t-transition
|
||||
@@ -285,7 +285,7 @@ QWeb.addDirective({
|
||||
}
|
||||
let eventsCode = events
|
||||
.map(function([eventName, mods, handlerValue, extraArgs]) {
|
||||
let params = "owner";
|
||||
let params = "context";
|
||||
if (extraArgs) {
|
||||
if (ctx.loopNumber) {
|
||||
let argId = ctx.generateID();
|
||||
@@ -293,20 +293,20 @@ QWeb.addDirective({
|
||||
// be set asynchronously later when the widget is ready, and the
|
||||
// context might be different.
|
||||
ctx.addLine(`let arg${argId} = ${ctx.formatExpression(extraArgs)};`);
|
||||
params = `owner, arg${argId}`;
|
||||
params = `context, arg${argId}`;
|
||||
} else {
|
||||
params = `owner, ${ctx.formatExpression(extraArgs)}`;
|
||||
params = `context, ${ctx.formatExpression(extraArgs)}`;
|
||||
}
|
||||
}
|
||||
let handler = `function (e) {if(!owner.__owl__.isMounted){return}`;
|
||||
let handler = `function (e) {if(!context.__owl__.isMounted){return}`;
|
||||
handler += mods
|
||||
.map(function(mod) {
|
||||
return T_COMPONENT_MODS_CODE[mod];
|
||||
})
|
||||
.join("");
|
||||
if (handlerValue) {
|
||||
handler += `const fn = owner['${handlerValue}'];`;
|
||||
handler += `if (fn) { fn.call(${params}, e); } else { owner.${handlerValue}; }`;
|
||||
handler += `const fn = context['${handlerValue}'];`;
|
||||
handler += `if (fn) { fn.call(${params}, e); } else { context.${handlerValue}; }`;
|
||||
}
|
||||
handler += `}`;
|
||||
return `vn.elm.addEventListener('${eventName}', ${handler});`;
|
||||
@@ -348,25 +348,9 @@ QWeb.addDirective({
|
||||
}
|
||||
|
||||
// SLOTS
|
||||
const varDefs: string[] = [];
|
||||
const hasSlots = node.childNodes.length;
|
||||
if (hasSlots) {
|
||||
ctx.rootContext.shouldTrackScope = true;
|
||||
for (let v of Object.values(ctx.variables)) {
|
||||
if (v["id"]) {
|
||||
varDefs.push(v["id"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let scopeVars;
|
||||
if (hasSlots) {
|
||||
let scope = ctx.scopeVars.length ? `Object.assign({}, scope)` : `{}`;
|
||||
let vars = varDefs.length ? `{${varDefs.join(",")}}` : "undefined";
|
||||
scopeVars = `${scope}, ${vars}`;
|
||||
} else {
|
||||
scopeVars = "undefined, undefined";
|
||||
}
|
||||
let scope = hasSlots ? `Object.assign(Object.create(context), scope)` : "undefined";
|
||||
|
||||
ctx.addIf(`w${componentID}`);
|
||||
|
||||
@@ -376,8 +360,7 @@ QWeb.addDirective({
|
||||
styleCode = `.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};w${componentID}.el.style=${tattStyle};});`;
|
||||
}
|
||||
ctx.addLine(
|
||||
`w${componentID}.__updateProps(props${componentID}, extra.fiber${scopeVars &&
|
||||
", " + scopeVars})${styleCode};`
|
||||
`w${componentID}.__updateProps(props${componentID}, extra.fiber, ${scope})${styleCode};`
|
||||
);
|
||||
ctx.addLine(`let pvnode = w${componentID}.__owl__.pvnode;`);
|
||||
if (registerCode) {
|
||||
@@ -439,7 +422,7 @@ QWeb.addDirective({
|
||||
}
|
||||
|
||||
ctx.addLine(
|
||||
`let fiber = w${componentID}.__prepare(extra.fiber, ${scopeVars}, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});`
|
||||
`let fiber = w${componentID}.__prepare(extra.fiber, ${scope}, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});`
|
||||
);
|
||||
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
||||
const insertHook = refExpr ? `insert(vn) {${refExpr}},` : "";
|
||||
|
||||
@@ -49,7 +49,6 @@ export class Fiber {
|
||||
inserter: (el: HTMLElement) => void | null;
|
||||
|
||||
scope: any;
|
||||
vars: any;
|
||||
|
||||
component: Component<any, any>;
|
||||
vnode: VNode | null = null;
|
||||
@@ -69,7 +68,6 @@ export class Fiber {
|
||||
|
||||
const __owl__ = component.__owl__;
|
||||
this.scope = __owl__.scope;
|
||||
this.vars = __owl__.vars;
|
||||
|
||||
this.root = parent ? parent.root : this;
|
||||
this.parent = parent;
|
||||
|
||||
Reference in New Issue
Block a user