mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
+54
-21
@@ -33,6 +33,19 @@ export interface Env {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface Fiber {
|
||||
force: boolean;
|
||||
rootFiber: Fiber | null;
|
||||
isCancelled: boolean;
|
||||
scope: any;
|
||||
vars: any;
|
||||
// component: Component<any, any, any>;
|
||||
// promises: any[];
|
||||
// vnode?: VNode;
|
||||
// handlers?: any;
|
||||
// mountedHandlers?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is mostly an internal detail of implementation. The Meta interface is
|
||||
* useful to typecheck and describe the internal keys used by Owl to manage the
|
||||
@@ -279,8 +292,9 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
if (__owl__.isMounted) {
|
||||
return;
|
||||
}
|
||||
const fiber = this.__createRootFiber(false);
|
||||
if (!__owl__.vnode) {
|
||||
const vnode = await this.__prepare();
|
||||
const vnode = await this.__prepareAndRender(fiber);
|
||||
if (__owl__.isDestroyed) {
|
||||
// component was destroyed before we get here...
|
||||
return;
|
||||
@@ -288,7 +302,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
this.__patch(vnode);
|
||||
} else if (renderBeforeRemount) {
|
||||
const patchQueue = [];
|
||||
await this.__render(false, patchQueue, undefined, undefined);
|
||||
await this.__render(fiber, patchQueue);
|
||||
this.__applyPatchQueue(<any[]>patchQueue);
|
||||
}
|
||||
target.appendChild(this.el!);
|
||||
@@ -323,10 +337,11 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
if (!__owl__.isMounted) {
|
||||
return;
|
||||
}
|
||||
const fiber = this.__createRootFiber(force);
|
||||
const patchQueue = [];
|
||||
|
||||
const renderId = ++__owl__.renderId;
|
||||
await this.__render(force, patchQueue, undefined, undefined);
|
||||
await this.__render(fiber, patchQueue);
|
||||
|
||||
if (__owl__.isMounted && renderId === __owl__.renderId) {
|
||||
// we only update the vnode and the actual DOM if no other rendering
|
||||
@@ -335,6 +350,25 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
}
|
||||
}
|
||||
|
||||
__createRootFiber(force): Fiber {
|
||||
const fiber: Fiber = {
|
||||
force,
|
||||
scope: undefined,
|
||||
vars: undefined,
|
||||
rootFiber: null,
|
||||
isCancelled: false
|
||||
};
|
||||
fiber.rootFiber = fiber;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
__createSubFiber(parent: Fiber, scope, vars): Fiber {
|
||||
const fiber = Object.create(parent);
|
||||
fiber.scope = scope;
|
||||
fiber.vars = vars;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the component. This operation is quite complex:
|
||||
* - it recursively destroy all children
|
||||
@@ -476,12 +510,12 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
*/
|
||||
async __updateProps(
|
||||
nextProps: Props,
|
||||
forceUpdate: boolean = false,
|
||||
parentFiber: Fiber,
|
||||
patchQueue?: any[],
|
||||
scope?: any,
|
||||
vars?: any
|
||||
): Promise<void> {
|
||||
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
||||
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
|
||||
if (shouldUpdate) {
|
||||
const defaultProps = (<any>this.constructor).defaultProps;
|
||||
if (defaultProps) {
|
||||
@@ -489,7 +523,8 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
}
|
||||
await this.willUpdateProps(nextProps);
|
||||
this.props = nextProps;
|
||||
await this.__render(forceUpdate, patchQueue, scope, vars);
|
||||
const fiber = this.__createSubFiber(parentFiber, scope, vars);
|
||||
await this.__render(fiber, patchQueue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,14 +538,20 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
__owl__.vnode = patch(target, vnode);
|
||||
}
|
||||
|
||||
__prepare(scope?: Object, vars?: any): Promise<VNode> {
|
||||
/**
|
||||
* 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
|
||||
* parent template.
|
||||
*/
|
||||
__prepare(parentFiber: Fiber, scope: any, vars: any): Promise<VNode> {
|
||||
const __owl__ = this.__owl__;
|
||||
const fiber = this.__createSubFiber(parentFiber, scope, vars);
|
||||
__owl__.renderProps = this.props;
|
||||
__owl__.renderPromise = this.__prepareAndRender(scope, vars);
|
||||
__owl__.renderPromise = this.__prepareAndRender(fiber);
|
||||
return __owl__.renderPromise;
|
||||
}
|
||||
|
||||
async __prepareAndRender(scope?: Object, vars?: any): Promise<VNode> {
|
||||
async __prepareAndRender(fiber: Fiber): Promise<VNode> {
|
||||
try {
|
||||
await this.willStart();
|
||||
} catch (e) {
|
||||
@@ -545,16 +586,10 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
}
|
||||
__owl__.render = qweb.render.bind(qweb, p._template);
|
||||
this.__observeState();
|
||||
|
||||
return this.__render(false, [], scope, vars);
|
||||
return this.__render(fiber, []);
|
||||
}
|
||||
|
||||
__render(
|
||||
force: boolean = false,
|
||||
patchQueue: any[] = [],
|
||||
scope?: Object,
|
||||
vars?: any
|
||||
): Promise<VNode> {
|
||||
__render(fiber: Fiber, patchQueue: any[] = []): Promise<VNode> {
|
||||
const __owl__ = this.__owl__;
|
||||
const promises: Promise<void>[] = [];
|
||||
const patch: any[] = [this];
|
||||
@@ -568,10 +603,8 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
promises,
|
||||
handlers: __owl__.boundHandlers,
|
||||
mountedHandlers: __owl__.mountedHandlers,
|
||||
forceUpdate: force,
|
||||
patchQueue,
|
||||
scope,
|
||||
vars
|
||||
fiber: fiber,
|
||||
patchQueue
|
||||
});
|
||||
} catch (e) {
|
||||
vnode = __owl__.vnode || h("div");
|
||||
|
||||
@@ -441,14 +441,15 @@ QWeb.addDirective({
|
||||
}
|
||||
}
|
||||
|
||||
let scopeVars = "";
|
||||
let scopeVars;
|
||||
if (hasSlots) {
|
||||
scopeVars += ctx.scopeVars.length ? `Object.assign({}, scope)` : varDefs.length ? `{}` : "";
|
||||
if (varDefs.length) {
|
||||
scopeVars += `, {${varDefs.join(",")}}`;
|
||||
}
|
||||
let scope = ctx.scopeVars.length ? `Object.assign({}, scope)` : `{}`;
|
||||
let vars = varDefs.length ? `{${varDefs.join(",")}}` : "undefined";
|
||||
scopeVars = `${scope}, ${vars}`;
|
||||
} else {
|
||||
scopeVars = "undefined, undefined";
|
||||
}
|
||||
ctx.addLine(`def${defID} = w${componentID}.__prepare(${scopeVars});`);
|
||||
ctx.addLine(`def${defID} = w${componentID}.__prepare(extra.fiber, ${scopeVars});`);
|
||||
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
||||
let registerCode = `c${ctx.parentNode}[_${dummyID}_index]=pvnode;`;
|
||||
if (shouldProxy) {
|
||||
@@ -472,7 +473,7 @@ QWeb.addDirective({
|
||||
ctx.addLine(`utils.validateProps(w${componentID}.constructor, props${componentID})`);
|
||||
}
|
||||
ctx.addLine(
|
||||
`def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, extra.forceUpdate, ${patchQueueCode}${scopeVars &&
|
||||
`def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, extra.fiber, ${patchQueueCode}${scopeVars &&
|
||||
", " + scopeVars});`
|
||||
);
|
||||
let keepAliveCode = "";
|
||||
|
||||
@@ -244,7 +244,7 @@ QWeb.addDirective({
|
||||
varCode = `{${content}}`;
|
||||
}
|
||||
ctx.addLine(
|
||||
`this.recursiveFns['${subTemplateName}'].call(this, context, Object.assign({}, extra, {parentNode: c${ctx.parentNode}, vars: ${varCode}, scope}));`
|
||||
`this.recursiveFns['${subTemplateName}'].call(this, context, Object.assign({}, extra, {parentNode: c${ctx.parentNode}, fiber: {vars: ${varCode}, scope}}));`
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
+5
-5
@@ -244,7 +244,7 @@ export class QWeb extends EventBus {
|
||||
this._processTemplate(elem);
|
||||
const template = {
|
||||
elem,
|
||||
fn: function (this: QWeb, context, extra) {
|
||||
fn: function(this: QWeb, context, extra) {
|
||||
const compiledFunction = this._compile(name, elem);
|
||||
template.fn = compiledFunction;
|
||||
return compiledFunction.call(this, context, extra);
|
||||
@@ -312,8 +312,8 @@ export class QWeb extends EventBus {
|
||||
* to render a full component tree, since this is an asynchronous operation.
|
||||
* This method can only render templates without components.
|
||||
*/
|
||||
renderToString(name: string, context: EvalContext = {}): string {
|
||||
const vnode = this.render(name, context);
|
||||
renderToString(name: string, context: EvalContext = {}, extra?: any): string {
|
||||
const vnode = this.render(name, context, extra);
|
||||
if (vnode.sel === undefined) {
|
||||
return vnode.text!;
|
||||
}
|
||||
@@ -357,12 +357,12 @@ export class QWeb extends EventBus {
|
||||
for (let v in parentContext.variables) {
|
||||
let variable = <any>parentContext.variables[v];
|
||||
if (variable.id) {
|
||||
ctx.addLine(`let ${variable.id} = extra.vars.${variable.id}`);
|
||||
ctx.addLine(`let ${variable.id} = extra.fiber.vars.${variable.id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parentContext) {
|
||||
ctx.addLine(" Object.assign(context, extra.scope);");
|
||||
ctx.addLine(" Object.assign(context, extra.fiber.scope);");
|
||||
}
|
||||
this._compileNode(elem, ctx);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Env } from "../component/component";
|
||||
import { Component, Env, Fiber } from "../component/component";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Connect function
|
||||
@@ -47,8 +47,7 @@ export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S>
|
||||
* Need to do this here so 'deep' can be overrided by subcomponent easily
|
||||
*/
|
||||
async __prepareAndRender(
|
||||
scope?: Object,
|
||||
vars?: any
|
||||
fiber: Fiber
|
||||
): ReturnType<Component<any, any, any>["__prepareAndRender"]> {
|
||||
const store = this.getStore(this.env);
|
||||
const ownProps = this.props || {};
|
||||
@@ -62,7 +61,7 @@ export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S>
|
||||
prevStoreProps: this.storeProps
|
||||
});
|
||||
(this.__owl__ as any).rev = observer.rev;
|
||||
return super.__prepareAndRender(scope, vars);
|
||||
return super.__prepareAndRender(fiber);
|
||||
}
|
||||
/**
|
||||
* We do not use the mounted hook here for a subtle reason: we want the
|
||||
|
||||
@@ -10,8 +10,6 @@ import { QWeb } from "./qweb/index";
|
||||
* The plan is to add a few other tags such as css, globalcss.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* XML tag helper for defining templates. With this, one can simply define
|
||||
* an inline template with just the template xml:
|
||||
|
||||
Reference in New Issue
Block a user