2019-02-02 18:17:21 +01:00
|
|
|
import { EventBus } from "./event_bus";
|
2019-04-16 22:59:54 +02:00
|
|
|
import { Observer } from "./observer";
|
2019-05-06 12:34:36 +02:00
|
|
|
import { QWeb, CompiledTemplate } from "./qweb_core";
|
2019-04-20 23:31:23 +02:00
|
|
|
import { h, patch, VNode } from "./vdom";
|
2019-03-14 09:03:39 +01:00
|
|
|
|
2019-05-08 11:23:48 +02:00
|
|
|
/**
|
|
|
|
|
* Owl Component System
|
|
|
|
|
*
|
|
|
|
|
* This file introduces a declarative and composable component system. It
|
|
|
|
|
* contains:
|
|
|
|
|
*
|
|
|
|
|
* - the Env interface (generic type for the environment)
|
|
|
|
|
* - the Meta interface (the owl specific metadata attached to a component)
|
|
|
|
|
* - the Component class
|
|
|
|
|
*/
|
|
|
|
|
|
2019-01-27 22:02:08 +01:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Types/helpers
|
|
|
|
|
//------------------------------------------------------------------------------
|
2019-01-31 13:19:30 +01:00
|
|
|
|
2019-03-14 12:51:37 +01:00
|
|
|
export interface Env {
|
2019-01-16 13:19:58 +01:00
|
|
|
qweb: QWeb;
|
2019-01-25 14:27:16 +01:00
|
|
|
}
|
|
|
|
|
|
2019-05-07 11:36:03 +02:00
|
|
|
export interface Meta<T extends Env, Props> {
|
2019-01-27 09:23:48 +01:00
|
|
|
readonly id: number;
|
2019-01-25 14:27:16 +01:00
|
|
|
vnode: VNode | null;
|
|
|
|
|
isStarted: boolean;
|
|
|
|
|
isMounted: boolean;
|
2019-01-26 18:20:49 +01:00
|
|
|
isDestroyed: boolean;
|
2019-02-05 14:16:51 +01:00
|
|
|
parent: Component<T, any, any> | null;
|
|
|
|
|
children: { [key: number]: Component<T, any, any> };
|
2019-01-27 22:16:00 +01:00
|
|
|
// children mapping: from templateID to widgetID
|
2019-01-28 15:51:20 +01:00
|
|
|
// should it be a map number => Widget?
|
2019-01-27 22:16:00 +01:00
|
|
|
cmap: { [key: number]: number };
|
2019-02-07 12:46:30 +01:00
|
|
|
|
|
|
|
|
renderId: number;
|
2019-03-12 11:42:46 +01:00
|
|
|
renderProps: Props | null;
|
2019-02-07 12:46:30 +01:00
|
|
|
renderPromise: Promise<VNode> | null;
|
2019-02-09 13:03:44 +01:00
|
|
|
boundHandlers: { [key: number]: any };
|
2019-04-18 10:10:18 +02:00
|
|
|
observer?: Observer;
|
2019-05-02 09:47:33 +02:00
|
|
|
render?: CompiledTemplate;
|
2019-01-16 13:19:58 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-27 22:02:08 +01:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Widget
|
|
|
|
|
//------------------------------------------------------------------------------
|
2019-04-25 16:44:48 +02:00
|
|
|
let nextId = 1;
|
2019-01-31 13:19:30 +01:00
|
|
|
|
2019-02-05 14:16:51 +01:00
|
|
|
export class Component<
|
2019-03-14 12:51:37 +01:00
|
|
|
T extends Env,
|
2019-03-22 09:28:56 +01:00
|
|
|
Props extends {},
|
2019-02-05 08:56:33 +01:00
|
|
|
State extends {}
|
|
|
|
|
> extends EventBus {
|
2019-04-13 14:14:34 +02:00
|
|
|
readonly __owl__: Meta<Env, Props>;
|
2019-02-02 11:41:19 +01:00
|
|
|
template: string = "default";
|
2019-02-03 09:42:35 +01:00
|
|
|
inlineTemplate: string | null = null;
|
2019-01-16 13:19:58 +01:00
|
|
|
|
2019-01-26 08:40:09 +01:00
|
|
|
get el(): HTMLElement | null {
|
2019-04-13 14:14:34 +02:00
|
|
|
return this.__owl__.vnode ? (<any>this).__owl__.vnode.elm : null;
|
2019-01-26 08:40:09 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-23 11:12:54 +01:00
|
|
|
env: T;
|
2019-04-18 10:10:18 +02:00
|
|
|
state?: State;
|
2019-01-29 20:49:51 +01:00
|
|
|
props: Props;
|
2019-02-04 21:09:17 +01:00
|
|
|
refs: {
|
2019-02-05 14:16:51 +01:00
|
|
|
[key: string]: Component<T, any, any> | HTMLElement | undefined;
|
2019-02-04 21:09:17 +01:00
|
|
|
} = {};
|
2019-01-16 13:19:58 +01:00
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
// Lifecycle
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
2019-03-19 13:46:18 +01:00
|
|
|
/**
|
|
|
|
|
* Creates an instance of Component.
|
|
|
|
|
*
|
|
|
|
|
* The root widget of a component tree needs an environment:
|
|
|
|
|
*
|
|
|
|
|
* ```javascript
|
|
|
|
|
* const root = new RootWidget(env, props);
|
|
|
|
|
* ```
|
|
|
|
|
*
|
|
|
|
|
* Every other widget simply needs a reference to its parent:
|
|
|
|
|
*
|
|
|
|
|
* ```javascript
|
|
|
|
|
* const child = new SomeWidget(parent, props);
|
|
|
|
|
* ```
|
|
|
|
|
*
|
|
|
|
|
* Note that most of the time, only the root widget needs to be created by
|
|
|
|
|
* hand. Other widgets should be created automatically by the framework (with
|
|
|
|
|
* the t-widget directive in a template)
|
|
|
|
|
*/
|
2019-02-05 14:16:51 +01:00
|
|
|
constructor(parent: Component<T, any, any> | T, props?: Props) {
|
2019-02-02 18:17:21 +01:00
|
|
|
super();
|
2019-02-03 09:42:35 +01:00
|
|
|
|
2019-01-29 20:49:51 +01:00
|
|
|
// is this a good idea?
|
|
|
|
|
// Pro: if props is empty, we can create easily a widget
|
|
|
|
|
// Con: this is not really safe
|
|
|
|
|
// Pro: but creating widget (by a template) is always unsafe anyway
|
2019-03-22 09:28:56 +01:00
|
|
|
this.props = <Props>props || <Props>{};
|
2019-04-25 16:44:48 +02:00
|
|
|
let id: number = nextId++;
|
2019-02-05 14:16:51 +01:00
|
|
|
let p: Component<T, any, any> | null = null;
|
|
|
|
|
if (parent instanceof Component) {
|
2019-01-25 14:27:16 +01:00
|
|
|
p = parent;
|
2019-01-26 20:33:23 +01:00
|
|
|
this.env = parent.env;
|
2019-04-13 14:14:34 +02:00
|
|
|
parent.__owl__.children[id] = this;
|
2019-01-22 10:38:44 +01:00
|
|
|
} else {
|
|
|
|
|
this.env = parent;
|
2019-01-16 13:19:58 +01:00
|
|
|
}
|
2019-04-13 14:14:34 +02:00
|
|
|
this.__owl__ = {
|
2019-01-27 13:50:53 +01:00
|
|
|
id: id,
|
2019-01-25 14:27:16 +01:00
|
|
|
vnode: null,
|
|
|
|
|
isStarted: false,
|
|
|
|
|
isMounted: false,
|
2019-01-26 18:20:49 +01:00
|
|
|
isDestroyed: false,
|
2019-01-25 14:27:16 +01:00
|
|
|
parent: p,
|
2019-01-27 22:16:00 +01:00
|
|
|
children: {},
|
2019-02-07 12:46:30 +01:00
|
|
|
cmap: {},
|
|
|
|
|
renderId: 1,
|
|
|
|
|
renderPromise: null,
|
2019-03-12 11:42:46 +01:00
|
|
|
renderProps: props || null,
|
2019-04-18 10:10:18 +02:00
|
|
|
boundHandlers: {}
|
2019-01-25 14:27:16 +01:00
|
|
|
};
|
2019-01-16 13:19:58 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-19 13:46:18 +01:00
|
|
|
/**
|
|
|
|
|
* willStart is an asynchronous hook that can be implemented to perform some
|
|
|
|
|
* action before the initial rendering of a component.
|
|
|
|
|
*
|
|
|
|
|
* It will be called exactly once before the initial rendering. It is useful
|
|
|
|
|
* in some cases, for example, to load external assets (such as a JS library)
|
|
|
|
|
* before the widget is rendered.
|
|
|
|
|
*
|
|
|
|
|
* Note that a slow willStart method will slow down the rendering of the user
|
|
|
|
|
* interface. Therefore, some effort should be made to make this method as
|
|
|
|
|
* fast as possible.
|
|
|
|
|
*
|
|
|
|
|
* Note: this method should not be called manually.
|
|
|
|
|
*/
|
2019-01-16 13:19:58 +01:00
|
|
|
async willStart() {}
|
|
|
|
|
|
2019-03-19 13:46:18 +01:00
|
|
|
/**
|
|
|
|
|
* mounted is a hook that is called each time a component is attached to the
|
|
|
|
|
* DOM. This is a good place to add some listeners, or to interact with the
|
|
|
|
|
* DOM, if the component needs to perform some measure for example.
|
|
|
|
|
*
|
|
|
|
|
* Note: this method should not be called manually.
|
|
|
|
|
*
|
|
|
|
|
* @see willUnmount
|
|
|
|
|
*/
|
2019-01-16 13:19:58 +01:00
|
|
|
mounted() {}
|
|
|
|
|
|
2019-04-09 13:51:04 +02:00
|
|
|
/**
|
|
|
|
|
* The willUpdateProps is an asynchronous hook, called just before new props
|
|
|
|
|
* are set. This is useful if the component needs some asynchronous task
|
|
|
|
|
* performed, depending on the props (for example, assuming that the props are
|
|
|
|
|
* some record Id, fetching the record data).
|
|
|
|
|
*
|
|
|
|
|
* This hook is not called during the first render (but willStart is called
|
|
|
|
|
* and performs a similar job).
|
|
|
|
|
*/
|
|
|
|
|
async willUpdateProps(nextProps: Props) {}
|
|
|
|
|
|
2019-04-05 16:30:35 +02:00
|
|
|
/**
|
|
|
|
|
* The willPatch hook is called just before the DOM patching process starts.
|
|
|
|
|
* It is not called on the initial render. This is useful to get some
|
|
|
|
|
* information which are in the DOM. For example, the current position of the
|
|
|
|
|
* scrollbar
|
2019-04-25 17:10:36 +02:00
|
|
|
*
|
2019-04-25 14:46:04 +02:00
|
|
|
* The return value of willPatch will be given to the patched function.
|
2019-04-05 16:30:35 +02:00
|
|
|
*/
|
2019-04-25 14:46:04 +02:00
|
|
|
willPatch(): any {}
|
2019-04-05 16:30:35 +02:00
|
|
|
|
2019-04-01 15:42:24 +02:00
|
|
|
/**
|
|
|
|
|
* This hook is called whenever a component did actually update its props,
|
|
|
|
|
* state or env.
|
|
|
|
|
*
|
|
|
|
|
* This method is not called on the initial render. It is useful to interact
|
|
|
|
|
* with the DOM (for example, through an external library) whenever the
|
|
|
|
|
* component was updated.
|
2019-04-05 23:28:21 +02:00
|
|
|
*
|
|
|
|
|
* Updating the widget state in this hook is possible, but not encouraged.
|
|
|
|
|
* One need to be careful, because updates here will cause rerender, which in
|
|
|
|
|
* turn will cause other calls to updated. So, we need to be particularly
|
|
|
|
|
* careful at avoiding endless cycles.
|
2019-04-25 14:46:04 +02:00
|
|
|
*
|
|
|
|
|
* The snapshot parameter is the result of the call to willPatch.
|
2019-04-01 15:42:24 +02:00
|
|
|
*/
|
2019-04-25 14:46:04 +02:00
|
|
|
patched(snapshot: any) {}
|
2019-04-01 15:42:24 +02:00
|
|
|
|
2019-03-19 13:46:18 +01:00
|
|
|
/**
|
2019-04-09 12:57:34 +02:00
|
|
|
* willUnmount is a hook that is called each time just before a component is
|
|
|
|
|
* unmounted from the DOM. This is a good place to remove some listeners, for
|
|
|
|
|
* example.
|
2019-03-19 13:46:18 +01:00
|
|
|
*
|
|
|
|
|
* Note: this method should not be called manually.
|
|
|
|
|
*
|
|
|
|
|
* @see mounted
|
|
|
|
|
*/
|
2019-01-16 13:19:58 +01:00
|
|
|
willUnmount() {}
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
// Public
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
2019-01-27 21:50:22 +01:00
|
|
|
async mount(target: HTMLElement): Promise<void> {
|
2019-04-10 10:45:27 +02:00
|
|
|
const vnode = await this._prepare();
|
2019-04-13 14:14:34 +02:00
|
|
|
if (this.__owl__.isDestroyed) {
|
2019-01-27 21:50:22 +01:00
|
|
|
// widget was destroyed before we get here...
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-02-07 12:10:44 +01:00
|
|
|
this._patch(vnode);
|
2019-01-25 11:12:20 +01:00
|
|
|
target.appendChild(this.el!);
|
|
|
|
|
|
|
|
|
|
if (document.body.contains(target)) {
|
2019-04-26 16:20:50 +02:00
|
|
|
this._callMounted();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_callMounted() {
|
|
|
|
|
const children = this.__owl__.children;
|
|
|
|
|
for (let id in children) {
|
|
|
|
|
const comp = children[id];
|
|
|
|
|
if (!comp.__owl__.isMounted && this.el!.contains(comp.el)) {
|
|
|
|
|
comp._callMounted();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.__owl__.isMounted = true;
|
|
|
|
|
this.mounted();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_callWillUnmount() {
|
|
|
|
|
this.willUnmount();
|
|
|
|
|
this.__owl__.isMounted = false;
|
|
|
|
|
const children = this.__owl__.children;
|
|
|
|
|
for (let id in children) {
|
|
|
|
|
const comp = children[id];
|
|
|
|
|
if (comp.__owl__.isMounted) {
|
|
|
|
|
comp._callWillUnmount();
|
|
|
|
|
}
|
2019-01-20 14:46:36 +01:00
|
|
|
}
|
2019-01-16 13:19:58 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-09 12:57:34 +02:00
|
|
|
unmount() {
|
2019-04-26 16:20:50 +02:00
|
|
|
if (this.__owl__.isMounted) {
|
|
|
|
|
this._callWillUnmount();
|
|
|
|
|
this.el!.remove();
|
2019-01-31 10:55:06 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-27 19:20:29 +02:00
|
|
|
async render(force: boolean = false, patchQueue?: any[]): Promise<void> {
|
2019-05-06 13:45:50 +02:00
|
|
|
if (!this.__owl__.isMounted) {
|
2019-03-19 13:46:18 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2019-05-08 15:21:33 +02:00
|
|
|
const shouldPatch: boolean = !patchQueue;
|
|
|
|
|
if (shouldPatch) {
|
2019-04-27 19:20:29 +02:00
|
|
|
patchQueue = [];
|
|
|
|
|
}
|
|
|
|
|
const renderVDom = this._render(force, patchQueue);
|
2019-04-13 14:14:34 +02:00
|
|
|
const renderId = this.__owl__.renderId;
|
2019-05-08 15:21:33 +02:00
|
|
|
await renderVDom;
|
2019-05-06 14:23:29 +02:00
|
|
|
|
2019-05-08 15:21:33 +02:00
|
|
|
if (
|
|
|
|
|
shouldPatch &&
|
|
|
|
|
this.__owl__.isMounted &&
|
|
|
|
|
renderId === this.__owl__.renderId
|
|
|
|
|
) {
|
2019-03-19 13:46:18 +01:00
|
|
|
// we only update the vnode and the actual DOM if no other rendering
|
|
|
|
|
// occurred between now and when the render method was initially called.
|
2019-05-08 15:21:33 +02:00
|
|
|
for (let i = 0; i < patchQueue!.length; i++) {
|
|
|
|
|
const patch = patchQueue![i];
|
|
|
|
|
patch.push(patch[0].willPatch());
|
2019-04-27 19:20:29 +02:00
|
|
|
}
|
2019-05-08 15:21:33 +02:00
|
|
|
for (let i = 0; i < patchQueue!.length; i++) {
|
|
|
|
|
const patch = patchQueue![i];
|
|
|
|
|
patch[0]._patch(patch[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (let i = patchQueue!.length - 1; i >= 0; i--) {
|
|
|
|
|
const patch = patchQueue![i];
|
|
|
|
|
patch[0].patched(patch[2]);
|
2019-04-27 19:20:29 +02:00
|
|
|
}
|
2019-03-19 13:46:18 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-16 23:03:51 +01:00
|
|
|
destroy() {
|
2019-04-13 14:14:34 +02:00
|
|
|
if (!this.__owl__.isDestroyed) {
|
2019-04-26 16:20:50 +02:00
|
|
|
const el = this.el;
|
2019-04-26 17:32:02 +02:00
|
|
|
this._destroy(this.__owl__.parent);
|
2019-04-26 16:20:50 +02:00
|
|
|
if (el) {
|
|
|
|
|
el.remove();
|
2019-01-27 15:40:10 +01:00
|
|
|
}
|
2019-01-16 13:19:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-26 17:32:02 +02:00
|
|
|
_destroy(parent) {
|
2019-04-26 16:20:50 +02:00
|
|
|
const isMounted = this.__owl__.isMounted;
|
|
|
|
|
if (isMounted) {
|
|
|
|
|
this.willUnmount();
|
|
|
|
|
this.__owl__.isMounted = false;
|
|
|
|
|
}
|
|
|
|
|
const children = Object.values(this.__owl__.children);
|
|
|
|
|
for (let child of children) {
|
2019-04-26 17:32:02 +02:00
|
|
|
child._destroy(this);
|
2019-04-26 16:20:50 +02:00
|
|
|
}
|
2019-04-26 17:32:02 +02:00
|
|
|
if (parent) {
|
2019-04-26 16:20:50 +02:00
|
|
|
let id = this.__owl__.id;
|
2019-04-26 17:32:02 +02:00
|
|
|
delete parent.__owl__.children[id];
|
2019-04-26 16:20:50 +02:00
|
|
|
this.__owl__.parent = null;
|
|
|
|
|
}
|
|
|
|
|
this.clear();
|
|
|
|
|
this.__owl__.isDestroyed = true;
|
|
|
|
|
delete this.__owl__.vnode;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-19 10:05:46 +01:00
|
|
|
shouldUpdate(nextProps: Props): boolean {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-19 11:48:30 +01:00
|
|
|
/**
|
|
|
|
|
* This method is the correct way to update the environment of a widget. Doing
|
|
|
|
|
* this will cause a full rerender of the widget and its children, so this is
|
|
|
|
|
* an operation that should not be done frequently.
|
|
|
|
|
*
|
|
|
|
|
* A good usecase for updating the environment would be to update some mostly
|
|
|
|
|
* static config keys, such as a boolean to determine if we are in mobile
|
|
|
|
|
* mode or not.
|
|
|
|
|
*/
|
|
|
|
|
async updateEnv(nextEnv: Partial<T>): Promise<void> {
|
2019-04-13 14:14:34 +02:00
|
|
|
if (this.__owl__.parent && this.__owl__.parent.env === this.env) {
|
2019-03-19 11:48:30 +01:00
|
|
|
this.env = Object.create(this.env);
|
|
|
|
|
}
|
|
|
|
|
Object.assign(this.env, nextEnv);
|
2019-04-13 14:14:34 +02:00
|
|
|
if (this.__owl__.isMounted) {
|
2019-04-01 15:42:24 +02:00
|
|
|
await this.render(true);
|
2019-03-21 10:37:49 +01:00
|
|
|
}
|
2019-03-19 11:48:30 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-18 10:15:50 +02:00
|
|
|
set(target: any, key: string | number, value: any) {
|
|
|
|
|
this.__owl__.observer!.set(target, key, value);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-16 13:19:58 +01:00
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
// Private
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
2019-04-29 16:49:37 +02:00
|
|
|
async _updateProps(
|
|
|
|
|
nextProps: Props,
|
|
|
|
|
forceUpdate: boolean = false,
|
2019-05-07 14:33:01 +02:00
|
|
|
patchQueue?: any[]
|
2019-04-29 16:49:37 +02:00
|
|
|
): Promise<void> {
|
|
|
|
|
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
|
|
|
|
if (shouldUpdate) {
|
|
|
|
|
await this.willUpdateProps(nextProps);
|
|
|
|
|
this.props = nextProps;
|
2019-05-09 13:11:55 +02:00
|
|
|
await this.render(forceUpdate, patchQueue);
|
2019-04-29 16:49:37 +02:00
|
|
|
}
|
2019-03-12 11:42:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_patch(vnode) {
|
2019-04-13 14:14:34 +02:00
|
|
|
this.__owl__.renderPromise = null;
|
2019-05-08 15:21:33 +02:00
|
|
|
const target = this.__owl__.vnode || document.createElement(vnode.sel!);
|
|
|
|
|
this.__owl__.vnode = patch(target, vnode);
|
2019-01-16 13:19:58 +01:00
|
|
|
}
|
2019-05-02 09:47:33 +02:00
|
|
|
_prepare(): Promise<VNode> {
|
2019-04-13 14:14:34 +02:00
|
|
|
this.__owl__.renderProps = this.props;
|
2019-05-02 09:47:33 +02:00
|
|
|
this.__owl__.renderPromise = this._prepareAndRender();
|
2019-04-13 14:14:34 +02:00
|
|
|
return this.__owl__.renderPromise;
|
2019-01-25 11:12:20 +01:00
|
|
|
}
|
|
|
|
|
|
2019-05-02 09:47:33 +02:00
|
|
|
async _prepareAndRender(): Promise<VNode> {
|
|
|
|
|
await this.willStart();
|
|
|
|
|
if (this.__owl__.isDestroyed) {
|
|
|
|
|
return Promise.resolve(h("div"));
|
|
|
|
|
}
|
|
|
|
|
this.__owl__.isStarted = true;
|
|
|
|
|
if (this.inlineTemplate) {
|
|
|
|
|
this.env.qweb.addTemplate(this.inlineTemplate, this.inlineTemplate, true);
|
|
|
|
|
}
|
|
|
|
|
this.__owl__.render = this.env.qweb.render.bind(
|
|
|
|
|
this.env.qweb,
|
|
|
|
|
this.inlineTemplate || this.template
|
|
|
|
|
);
|
|
|
|
|
this._observeState();
|
|
|
|
|
return this._render();
|
|
|
|
|
}
|
2019-04-29 16:49:37 +02:00
|
|
|
async _render(
|
|
|
|
|
force: boolean = false,
|
|
|
|
|
patchQueue: any[] = []
|
|
|
|
|
): Promise<VNode> {
|
2019-04-13 14:14:34 +02:00
|
|
|
this.__owl__.renderId++;
|
2019-01-25 10:25:42 +01:00
|
|
|
const promises: Promise<void>[] = [];
|
2019-05-08 15:21:33 +02:00
|
|
|
const patch: any[] = [this];
|
|
|
|
|
if (this.__owl__.isMounted) {
|
|
|
|
|
patchQueue.push(patch);
|
|
|
|
|
}
|
2019-04-18 10:10:18 +02:00
|
|
|
if (this.__owl__.observer) {
|
|
|
|
|
this.__owl__.observer.allowMutations = false;
|
|
|
|
|
}
|
2019-05-02 09:47:33 +02:00
|
|
|
let vnode = this.__owl__.render!(this, {
|
2019-02-09 13:03:44 +01:00
|
|
|
promises,
|
2019-04-13 14:14:34 +02:00
|
|
|
handlers: this.__owl__.boundHandlers,
|
2019-04-27 19:20:29 +02:00
|
|
|
forceUpdate: force,
|
2019-04-29 16:49:37 +02:00
|
|
|
patchQueue
|
2019-02-09 13:03:44 +01:00
|
|
|
});
|
2019-05-08 15:21:33 +02:00
|
|
|
patch.push(vnode);
|
2019-04-18 10:10:18 +02:00
|
|
|
if (this.__owl__.observer) {
|
|
|
|
|
this.__owl__.observer.allowMutations = true;
|
|
|
|
|
}
|
2019-01-26 08:40:09 +01:00
|
|
|
|
|
|
|
|
// this part is critical for the patching process to be done correctly. The
|
|
|
|
|
// tricky part is that a child widget can be rerendered on its own, which
|
|
|
|
|
// will update its own vnode representation without the knowledge of the
|
|
|
|
|
// parent widget. With this, we make sure that the parent widget will be
|
|
|
|
|
// able to patch itself properly after
|
2019-04-13 14:14:34 +02:00
|
|
|
vnode.key = this.__owl__.id;
|
|
|
|
|
this.__owl__.renderProps = this.props;
|
|
|
|
|
this.__owl__.renderPromise = Promise.all(promises).then(() => vnode);
|
|
|
|
|
return this.__owl__.renderPromise;
|
2019-01-25 10:25:42 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-26 08:40:09 +01:00
|
|
|
/**
|
|
|
|
|
* Only called by qweb t-widget directive
|
|
|
|
|
*/
|
2019-01-30 14:56:47 +01:00
|
|
|
_mount(vnode: VNode, elm: HTMLElement): VNode {
|
2019-04-13 14:14:34 +02:00
|
|
|
this.__owl__.vnode = patch(elm, vnode);
|
2019-04-26 17:32:02 +02:00
|
|
|
if (
|
|
|
|
|
this.__owl__.parent &&
|
|
|
|
|
this.__owl__.parent.__owl__.isMounted &&
|
|
|
|
|
!this.__owl__.isMounted
|
|
|
|
|
) {
|
|
|
|
|
this._callMounted();
|
|
|
|
|
}
|
2019-04-13 14:14:34 +02:00
|
|
|
return this.__owl__.vnode;
|
2019-01-31 15:14:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__mount() {
|
2019-04-26 17:32:02 +02:00
|
|
|
if (!this.__owl__.isMounted) {
|
|
|
|
|
this.__owl__.isMounted = true;
|
|
|
|
|
this.mounted();
|
2019-01-25 11:12:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-16 22:59:54 +02:00
|
|
|
_observeState() {
|
2019-04-18 10:10:18 +02:00
|
|
|
if (this.state) {
|
|
|
|
|
this.__owl__.observer = new Observer();
|
2019-04-16 22:59:54 +02:00
|
|
|
this.__owl__.observer.observe(this.state);
|
2019-04-17 10:07:31 +02:00
|
|
|
this.__owl__.observer.notifyCB = this.render.bind(this);
|
2019-04-16 22:59:54 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-01-16 13:19:58 +01:00
|
|
|
}
|