mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[DOC] component: improve internal documentation
This commit is contained in:
+71
-13
@@ -38,15 +38,21 @@ export interface Env {
|
|||||||
* useful to typecheck and describe the internal keys used by Owl to manage the
|
* useful to typecheck and describe the internal keys used by Owl to manage the
|
||||||
* component tree.
|
* component tree.
|
||||||
*/
|
*/
|
||||||
export interface Meta<T extends Env, Props> {
|
interface Internal<T extends Env, Props> {
|
||||||
|
// each component has a unique id, useful mostly to handle parent/child
|
||||||
|
// relationships
|
||||||
readonly id: number;
|
readonly id: number;
|
||||||
vnode: VNode | null;
|
vnode: VNode | null;
|
||||||
isMounted: boolean;
|
isMounted: boolean;
|
||||||
isDestroyed: boolean;
|
isDestroyed: boolean;
|
||||||
|
|
||||||
|
// parent and children keys are obviously useful to setup the parent-children
|
||||||
|
// relationship.
|
||||||
parent: Component<T, any, any> | null;
|
parent: Component<T, any, any> | null;
|
||||||
children: { [key: number]: Component<T, any, any> };
|
children: { [key: number]: Component<T, any, any> };
|
||||||
// children mapping: from templateID to componentID
|
// children mapping: from templateID to componentID. templateID identifies a
|
||||||
// should it be a map number => Component?
|
// place in a template. The t-component directive needs it to be able to get
|
||||||
|
// the component instance back whenever the template is rerendered.
|
||||||
cmap: { [key: number]: number };
|
cmap: { [key: number]: number };
|
||||||
|
|
||||||
renderId: number;
|
renderId: number;
|
||||||
@@ -58,10 +64,10 @@ export interface Meta<T extends Env, Props> {
|
|||||||
renderPromise: Promise<VNode> | null;
|
renderPromise: Promise<VNode> | null;
|
||||||
|
|
||||||
boundHandlers: { [key: number]: any };
|
boundHandlers: { [key: number]: any };
|
||||||
observer?: Observer;
|
observer: Observer | null;
|
||||||
render?: CompiledTemplate;
|
render: CompiledTemplate | null;
|
||||||
mountedHandlers: { [key: number]: Function };
|
mountedHandlers: { [key: number]: Function };
|
||||||
classObj?: { [key: string]: boolean };
|
classObj: { [key: string]: boolean } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a component does not define explicitely a template
|
// If a component does not define explicitely a template
|
||||||
@@ -76,7 +82,7 @@ const TEMPLATE_MAP: { [key: number]: { [name: string]: string } } = {};
|
|||||||
let nextId = 1;
|
let nextId = 1;
|
||||||
|
|
||||||
export class Component<T extends Env, Props extends {}, State extends {}> {
|
export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||||
readonly __owl__: Meta<Env, Props>;
|
readonly __owl__: Internal<Env, Props>;
|
||||||
template?: string;
|
template?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,7 +177,10 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
renderPromise: null,
|
renderPromise: null,
|
||||||
renderProps: props || null,
|
renderProps: props || null,
|
||||||
boundHandlers: {},
|
boundHandlers: {},
|
||||||
mountedHandlers: {}
|
mountedHandlers: {},
|
||||||
|
observer: null,
|
||||||
|
render: null,
|
||||||
|
classObj: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,6 +299,10 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unmount method is the opposite of the mount method. It is useful
|
||||||
|
* to call willUnmount calls and remove the component from the DOM.
|
||||||
|
*/
|
||||||
unmount() {
|
unmount() {
|
||||||
if (this.__owl__.isMounted) {
|
if (this.__owl__.isMounted) {
|
||||||
this.__callWillUnmount();
|
this.__callWillUnmount();
|
||||||
@@ -297,6 +310,15 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The render method is the main entry point to render a component (once it
|
||||||
|
* is ready. This method is not initially called when the component is
|
||||||
|
* rendered the first time).
|
||||||
|
*
|
||||||
|
* This method will cause all its sub components to potentially rerender
|
||||||
|
* themselves. Note that `render` is not called if a component is updated via
|
||||||
|
* its props.
|
||||||
|
*/
|
||||||
async render(force: boolean = false): Promise<void> {
|
async render(force: boolean = false): Promise<void> {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
if (!__owl__.isMounted) {
|
if (!__owl__.isMounted) {
|
||||||
@@ -384,6 +406,17 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
// Private
|
// Private
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private helper to perform a full destroy, from the point of view of an Owl
|
||||||
|
* component. It does not remove the el (this is done only once on the top
|
||||||
|
* level destroyed component, for performance reasons).
|
||||||
|
*
|
||||||
|
* The job of this method is mostly to call willUnmount hooks, and to perform
|
||||||
|
* all necessary internal cleanup.
|
||||||
|
*
|
||||||
|
* Note that it does not call the __callWillUnmount method to avoid visiting
|
||||||
|
* all children many times.
|
||||||
|
*/
|
||||||
__destroy(parent: Component<any, any, any> | null) {
|
__destroy(parent: Component<any, any, any> | null) {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
const isMounted = __owl__.isMounted;
|
const isMounted = __owl__.isMounted;
|
||||||
@@ -438,6 +471,10 @@ export class Component<T extends Env, Props extends {}, State 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(
|
async __updateProps(
|
||||||
nextProps: Props,
|
nextProps: Props,
|
||||||
forceUpdate: boolean = false,
|
forceUpdate: boolean = false,
|
||||||
@@ -457,12 +494,13 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main patching method. We call the virtual dom patch method here to convert
|
||||||
|
* a virtual dom vnode into some actual dom.
|
||||||
|
*/
|
||||||
__patch(vnode) {
|
__patch(vnode) {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
const target = __owl__.vnode || document.createElement(vnode.sel!);
|
const target = __owl__.vnode || document.createElement(vnode.sel!);
|
||||||
if (this.__owl__.classObj) {
|
|
||||||
(<any>vnode).data.class = Object.assign((<any>vnode).data.class || {}, this.__owl__.classObj);
|
|
||||||
}
|
|
||||||
__owl__.vnode = patch(target, vnode);
|
__owl__.vnode = patch(target, vnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,6 +593,14 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
// parent component. With this, we make sure that the parent component will be
|
// parent component. With this, we make sure that the parent component will be
|
||||||
// able to patch itself properly after
|
// able to patch itself properly after
|
||||||
vnode.key = __owl__.id;
|
vnode.key = __owl__.id;
|
||||||
|
|
||||||
|
// we applly here the class information described on the component by the
|
||||||
|
// template (so, something like <MyComponent class="..."/>) to the actual
|
||||||
|
// root vnode
|
||||||
|
if (__owl__.classObj) {
|
||||||
|
vnode.data.class = Object.assign(vnode.data.class || {}, __owl__.classObj);
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.all(promises).then(() => vnode);
|
return Promise.all(promises).then(() => vnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,6 +630,10 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the observe feature on the state. We only create an observer if
|
||||||
|
* there is some state to be observed.
|
||||||
|
*/
|
||||||
__observeState() {
|
__observeState() {
|
||||||
if (this.state) {
|
if (this.state) {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
@@ -644,13 +694,21 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
|||||||
// Error handling
|
// Error handling
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
function errorHandler(error, component) {
|
/**
|
||||||
|
* This is the global error handler for errors occurring in Owl main lifecycle
|
||||||
|
* methods. Caught errors are triggered on the QWeb instance, and are
|
||||||
|
* potentially given to some parent component which implements `catchError`.
|
||||||
|
*
|
||||||
|
* If there are no such component, we destroy everything. This is better than
|
||||||
|
* being in a corrupted state.
|
||||||
|
*/
|
||||||
|
function errorHandler(error: Error, component: Component<any, any, any>) {
|
||||||
let canCatch = false;
|
let canCatch = false;
|
||||||
let qweb = component.env.qweb;
|
let qweb = component.env.qweb;
|
||||||
let root = component;
|
let root = component;
|
||||||
while (component && !(canCatch = component.catchError !== Component.prototype.catchError)) {
|
while (component && !(canCatch = component.catchError !== Component.prototype.catchError)) {
|
||||||
root = component;
|
root = component;
|
||||||
component = component.__owl__.parent;
|
component = component.__owl__.parent!;
|
||||||
}
|
}
|
||||||
console.error(error);
|
console.error(error);
|
||||||
// we trigger error on QWeb so it can be logged/handled
|
// we trigger error on QWeb so it can be logged/handled
|
||||||
|
|||||||
Reference in New Issue
Block a user