mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
committed by
Géry Debongnie
parent
9400c0adad
commit
6e5d6aa226
+21
-12
@@ -65,6 +65,12 @@ interface Internal<T extends Env, Props> {
|
|||||||
// its children, those that are not used anymore and thus can be destroyed
|
// its children, those that are not used anymore and thus can be destroyed
|
||||||
parentLastFiberId: number;
|
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
|
||||||
|
// store that information in case the component would be re-rendered later on.
|
||||||
|
scope: any;
|
||||||
|
vars: any;
|
||||||
|
|
||||||
boundHandlers: { [key: number]: any };
|
boundHandlers: { [key: number]: any };
|
||||||
observer: Observer | null;
|
observer: Observer | null;
|
||||||
renderFn: CompiledTemplate;
|
renderFn: CompiledTemplate;
|
||||||
@@ -184,7 +190,9 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
observer: null,
|
observer: null,
|
||||||
renderFn: qweb.render.bind(qweb, template),
|
renderFn: qweb.render.bind(qweb, template),
|
||||||
classObj: null,
|
classObj: null,
|
||||||
refs: null
|
refs: null,
|
||||||
|
scope: null,
|
||||||
|
vars: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,11 +295,13 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) {
|
if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) {
|
||||||
let message = `Component '${this.constructor.name}' cannot be mounted: the target is not a valid DOM node.`;
|
let message = `Component '${
|
||||||
|
this.constructor.name
|
||||||
|
}' cannot be mounted: the target is not a valid DOM node.`;
|
||||||
message += `\nMaybe the DOM is not ready yet? (in that case, you can use owl.utils.whenReady)`;
|
message += `\nMaybe the DOM is not ready yet? (in that case, you can use owl.utils.whenReady)`;
|
||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
const fiber = new Fiber(null, this, undefined, undefined, false, target);
|
const fiber = new Fiber(null, this, false, target);
|
||||||
if (!__owl__.vnode) {
|
if (!__owl__.vnode) {
|
||||||
this.__prepareAndRender(fiber);
|
this.__prepareAndRender(fiber);
|
||||||
} else {
|
} else {
|
||||||
@@ -335,7 +345,7 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
// currentFiber that is already rendered (isRendered is true), so we are
|
// currentFiber that is already rendered (isRendered is true), so we are
|
||||||
// about to be mounted
|
// about to be mounted
|
||||||
const isMounted = __owl__.isMounted;
|
const isMounted = __owl__.isMounted;
|
||||||
const fiber = new Fiber(null, this, undefined, undefined, force, null);
|
const fiber = new Fiber(null, this, force, null);
|
||||||
Promise.resolve().then(() => {
|
Promise.resolve().then(() => {
|
||||||
if (__owl__.isMounted || !isMounted) {
|
if (__owl__.isMounted || !isMounted) {
|
||||||
// we are mounted (__owl__.isMounted), or if we are currently being
|
// we are mounted (__owl__.isMounted), or if we are currently being
|
||||||
@@ -476,16 +486,13 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
* The __updateProps method is called by the t-component directive whenever
|
* The __updateProps method is called by the t-component directive whenever
|
||||||
* it updates a component (so, when the parent template is rerendered).
|
* it updates a component (so, when the parent template is rerendered).
|
||||||
*/
|
*/
|
||||||
async __updateProps(
|
async __updateProps(nextProps: Props, parentFiber: Fiber, scope: any, vars: any): Promise<void> {
|
||||||
nextProps: Props,
|
this.__owl__.scope = scope;
|
||||||
parentFiber: Fiber,
|
this.__owl__.vars = vars;
|
||||||
scope: any,
|
|
||||||
vars: any,
|
|
||||||
): Promise<void> {
|
|
||||||
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
|
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
|
||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force, null);
|
const fiber = new Fiber(parentFiber, this, parentFiber.force, null);
|
||||||
if (!parentFiber.child) {
|
if (!parentFiber.child) {
|
||||||
parentFiber.child = fiber;
|
parentFiber.child = fiber;
|
||||||
} else {
|
} else {
|
||||||
@@ -529,7 +536,9 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
* parent template.
|
* parent template.
|
||||||
*/
|
*/
|
||||||
__prepare(parentFiber: Fiber, scope: any, vars: any) {
|
__prepare(parentFiber: Fiber, scope: any, vars: any) {
|
||||||
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force, null);
|
this.__owl__.scope = scope;
|
||||||
|
this.__owl__.vars = vars;
|
||||||
|
const fiber = new Fiber(parentFiber, this, parentFiber.force, null);
|
||||||
fiber.shouldPatch = false;
|
fiber.shouldPatch = false;
|
||||||
if (!parentFiber.child) {
|
if (!parentFiber.child) {
|
||||||
parentFiber.child = fiber;
|
parentFiber.child = fiber;
|
||||||
|
|||||||
@@ -62,17 +62,19 @@ export class Fiber {
|
|||||||
|
|
||||||
error?: Error;
|
error?: Error;
|
||||||
|
|
||||||
constructor(parent: Fiber | null, component: Component<any, any>, scope, vars, force, target) {
|
constructor(parent: Fiber | null, component: Component<any, any>, force, target) {
|
||||||
this.force = force;
|
|
||||||
this.scope = scope;
|
|
||||||
this.vars = vars;
|
|
||||||
this.component = component;
|
this.component = component;
|
||||||
|
this.force = force;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
|
|
||||||
|
const __owl__ = component.__owl__;
|
||||||
|
this.scope = __owl__.scope;
|
||||||
|
this.vars = __owl__.vars;
|
||||||
|
|
||||||
this.root = parent ? parent.root : this;
|
this.root = parent ? parent.root : this;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
|
||||||
let oldFiber = component.__owl__.currentFiber;
|
let oldFiber = __owl__.currentFiber;
|
||||||
if (oldFiber && !oldFiber.isCompleted) {
|
if (oldFiber && !oldFiber.isCompleted) {
|
||||||
if (oldFiber.root === oldFiber && !parent) {
|
if (oldFiber.root === oldFiber && !parent) {
|
||||||
// both oldFiber and this fiber are root fibers
|
// both oldFiber and this fiber are root fibers
|
||||||
@@ -85,7 +87,7 @@ export class Fiber {
|
|||||||
|
|
||||||
this.root.counter++;
|
this.root.counter++;
|
||||||
|
|
||||||
component.__owl__.currentFiber = this;
|
__owl__.currentFiber = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2236,6 +2236,36 @@ describe("other directives with t-component", () => {
|
|||||||
await nextTick();
|
await nextTick();
|
||||||
expect(normalize(fixture.innerHTML)).toBe("<div><span>hey</span></div>");
|
expect(normalize(fixture.innerHTML)).toBe("<div><span>hey</span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-foreach with t-component, and update", async () => {
|
||||||
|
class Child extends Widget {
|
||||||
|
static template = xml`
|
||||||
|
<span>
|
||||||
|
<t t-esc="state.val"/>
|
||||||
|
<t t-esc="props.val"/>
|
||||||
|
</span>`;
|
||||||
|
state = useState({ val: 'A' });
|
||||||
|
mounted() {
|
||||||
|
this.state.val = 'B';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ParentWidget extends Widget {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-foreach="Array(2)" t-as="n" t-key="n_index">
|
||||||
|
<Child val="n_index"/>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = new ParentWidget();
|
||||||
|
await widget.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>A0</span><span>A1</span></div>");
|
||||||
|
|
||||||
|
await nextTick(); // wait for changes triggered in mounted to be applied
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("random stuff/miscellaneous", () => {
|
describe("random stuff/miscellaneous", () => {
|
||||||
@@ -4206,6 +4236,62 @@ describe("t-slot directive", () => {
|
|||||||
|
|
||||||
expect(fixture.innerHTML).toBe(`<div><span>1</span><span>2</span></div>`);
|
expect(fixture.innerHTML).toBe(`<div><span>1</span><span>2</span></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("slots in t-foreach and re-rendering", async () => {
|
||||||
|
class Child extends Widget {
|
||||||
|
static template = xml`<span><t t-esc="state.val"/><t t-slot="default"/></span>`;
|
||||||
|
state = useState({ val: 'A' });
|
||||||
|
mounted() {
|
||||||
|
this.state.val = 'B';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Parent extends Widget {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-foreach="Array(2)" t-as="n" t-key="n_index">
|
||||||
|
<Child><t t-esc="n_index"/></Child>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>A0</span><span>A1</span></div>");
|
||||||
|
|
||||||
|
await nextTick(); // wait for the changes triggered in mounted to be applied
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots in t-foreach with t-set and re-rendering", async () => {
|
||||||
|
class Child extends Widget {
|
||||||
|
static template = xml`
|
||||||
|
<span>
|
||||||
|
<t t-esc="state.val"/>
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</span>`;
|
||||||
|
state = useState({ val: 'A' });
|
||||||
|
mounted() {
|
||||||
|
this.state.val = 'B';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ParentWidget extends Widget {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-foreach="Array(2)" t-as="n" t-key="n_index">
|
||||||
|
<t t-set="dummy" t-value="n_index"/>
|
||||||
|
<Child><t t-esc="dummy"/></Child>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = new ParentWidget();
|
||||||
|
await widget.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>A0</span><span>A1</span></div>");
|
||||||
|
|
||||||
|
await nextTick(); // wait for changes triggered in mounted to be applied
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("t-model directive", () => {
|
describe("t-model directive", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user