mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[REF] app: reorganize startup code
The goal of this refactoring is to delay the instantiation of the component node until we are ready to mount the component. This is useful so we know if a component will be attached or mounted (which means that it needs to have a template, or not)
This commit is contained in:
+39
-33
@@ -53,7 +53,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Root<P extends Props, E> {
|
interface Root<P extends Props, E> {
|
||||||
node: ComponentNode<P, E>;
|
node: ComponentNode<P, E> | null;
|
||||||
mount(target: HTMLElement | ShadowRoot, options?: MountOptions): Promise<Component<P, E>>;
|
mount(target: HTMLElement | ShadowRoot, options?: MountOptions): Promise<Component<P, E>>;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
@@ -74,8 +74,8 @@ export class App<
|
|||||||
props: P;
|
props: P;
|
||||||
env: E;
|
env: E;
|
||||||
scheduler = new Scheduler();
|
scheduler = new Scheduler();
|
||||||
subRoots: Set<ComponentNode> = new Set();
|
subRoots: Set<Root<any, any>> = new Set();
|
||||||
root: ComponentNode<P, E> | null = null;
|
root: Root<P, E> | null = null;
|
||||||
warnIfNoStaticProps: boolean;
|
warnIfNoStaticProps: boolean;
|
||||||
|
|
||||||
constructor(Root: ComponentConstructor<P, E>, config: AppConfig<P, E> = {}) {
|
constructor(Root: ComponentConstructor<P, E>, config: AppConfig<P, E> = {}) {
|
||||||
@@ -101,10 +101,8 @@ export class App<
|
|||||||
target: HTMLElement | ShadowRoot,
|
target: HTMLElement | ShadowRoot,
|
||||||
options?: MountOptions
|
options?: MountOptions
|
||||||
): Promise<Component<P, E> & InstanceType<T>> {
|
): Promise<Component<P, E> & InstanceType<T>> {
|
||||||
const root = this.createRoot(this.Root, { props: this.props });
|
this.root = this.createRoot(this.Root, { props: this.props });
|
||||||
this.root = root.node;
|
return this.root.mount(target, options) as any;
|
||||||
this.subRoots.delete(root.node);
|
|
||||||
return root.mount(target, options) as any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createRoot<Props extends object, SubEnv = any>(
|
createRoot<Props extends object, SubEnv = any>(
|
||||||
@@ -112,25 +110,24 @@ export class App<
|
|||||||
config: RootConfig<Props, SubEnv> = {}
|
config: RootConfig<Props, SubEnv> = {}
|
||||||
): Root<Props, SubEnv> {
|
): Root<Props, SubEnv> {
|
||||||
const props = config.props || ({} as Props);
|
const props = config.props || ({} as Props);
|
||||||
// hack to make sure the sub root get the sub env if necessary. for owl 3,
|
|
||||||
// would be nice to rethink the initialization process to make sure that
|
|
||||||
// we can create a ComponentNode and give it explicitely the env, instead
|
|
||||||
// of looking it up in the app
|
|
||||||
const env = this.env;
|
const env = this.env;
|
||||||
if (config.env) {
|
const root: Root<Props, SubEnv> = {
|
||||||
this.env = config.env as any;
|
node: null,
|
||||||
}
|
|
||||||
|
|
||||||
const restore = saveCurrent();
|
|
||||||
const node = this.makeNode(Root, props);
|
|
||||||
restore();
|
|
||||||
if (config.env) {
|
|
||||||
this.env = env;
|
|
||||||
}
|
|
||||||
this.subRoots.add(node);
|
|
||||||
return {
|
|
||||||
node,
|
|
||||||
mount: (target: HTMLElement | ShadowRoot, options?: MountOptions) => {
|
mount: (target: HTMLElement | ShadowRoot, options?: MountOptions) => {
|
||||||
|
// hack to make sure the sub root get the sub env if necessary. for owl 3,
|
||||||
|
// would be nice to rethink the initialization process to make sure that
|
||||||
|
// we can create a ComponentNode and give it explicitely the env, instead
|
||||||
|
// of looking it up in the app
|
||||||
|
if (config.env) {
|
||||||
|
this.env = config.env as any;
|
||||||
|
}
|
||||||
|
const restore = saveCurrent();
|
||||||
|
const node = this.makeNode(Root, props);
|
||||||
|
root.node = node;
|
||||||
|
restore();
|
||||||
|
if (config.env) {
|
||||||
|
this.env = env;
|
||||||
|
}
|
||||||
App.validateTarget(target);
|
App.validateTarget(target);
|
||||||
if (this.dev) {
|
if (this.dev) {
|
||||||
validateProps(Root, props, { __owl__: { app: this } });
|
validateProps(Root, props, { __owl__: { app: this } });
|
||||||
@@ -139,11 +136,16 @@ export class App<
|
|||||||
return prom;
|
return prom;
|
||||||
},
|
},
|
||||||
destroy: () => {
|
destroy: () => {
|
||||||
this.subRoots.delete(node);
|
this.subRoots.delete(root);
|
||||||
node.destroy();
|
if (root.node) {
|
||||||
this.scheduler.processTasks();
|
root.node?.destroy();
|
||||||
|
this.scheduler.processTasks();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.subRoots.add(root);
|
||||||
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeNode(Component: ComponentConstructor, props: any): ComponentNode {
|
makeNode(Component: ComponentConstructor, props: any): ComponentNode {
|
||||||
@@ -178,13 +180,17 @@ export class App<
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.root) {
|
const roots = [...this.subRoots].reverse();
|
||||||
for (let subroot of this.subRoots) {
|
for (let root of roots) {
|
||||||
subroot.destroy();
|
root.destroy();
|
||||||
}
|
|
||||||
this.root.destroy();
|
|
||||||
this.scheduler.processTasks();
|
|
||||||
}
|
}
|
||||||
|
// if (this.root) {
|
||||||
|
// for (let subroot of this.subRoots) {
|
||||||
|
// subroot.destroy();
|
||||||
|
// }
|
||||||
|
// this.root.destroy();
|
||||||
|
this.scheduler.processTasks();
|
||||||
|
// }
|
||||||
apps.delete(this);
|
apps.delete(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,17 +94,6 @@ exports[`subroot can create a root in a setup function, then use a hook 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`subroot can create a root in a setup function, then use a hook 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return text(\`c\`);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`subroot can mount subroot 1`] = `
|
exports[`subroot can mount subroot 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -702,7 +702,7 @@ describe("props validation", () => {
|
|||||||
const app = new App(Parent, { test: true });
|
const app = new App(Parent, { test: true });
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("12");
|
expect(fixture.innerHTML).toBe("12");
|
||||||
expect(app.root!.subscriptions).toEqual([{ keys: ["otherValue"], target: obj }]);
|
expect(app.root!.node!.subscriptions).toEqual([{ keys: ["otherValue"], target: obj }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("props are validated whenever component is updated", async () => {
|
test("props are validated whenever component is updated", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user