Compare commits

..

3 Commits

Author SHA1 Message Date
Samuel Degueldre abd2b42307 [IMP] slots: make defaultContent of slot available to slot as zero
In some cases, it is useful for a slot to be able to keep the default
content of the slot it is supposed to replace and simply add extra
content instead of simply replacing it entirely. This commit makes the
default content of a slot available in the context as `zero` similar to
how t-call can access the content that was inside the t-call.
2023-01-09 10:50:57 +01:00
Samuel Degueldre 3d40533de1 [FIX] t-call-context: fix capture making component available in ctx
Previously, when using a component with a slot within a t-call with
t-call-context, the component would become available again inside the
slot despite the t-call-context. This was caused by the fact that the
capture helper function creates an object with the component as its
prototype which is incorrect. It should just use the previous context as
its prototype.
2023-01-09 09:28:10 +01:00
Samuel Degueldre 39329f80b2 [FIX] reactivity: don't subscribe to keys when making reactive
When attempting to create a reactive object, we first check if the
target can be made reactive, this is done with Object.toString, which
internally reads the Symbol.toStringTag on the underlying object. When
trying to make a reactive object from another, for example when
reobserving a reactive or when reading a reactive object from the
context of another, this would read the subscribe the original object to
the Symbol.toStringTag property.

This commit fixes that by calling Object.toString on the underlying
target object where applicable.
2022-12-08 14:22:39 +01:00
16 changed files with 220 additions and 315 deletions
+2 -2
View File
@@ -1140,7 +1140,7 @@ export class CodeGenerator {
if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx, this)`);
this.define(ctxStr, `capture(ctx)`);
}
let slotStr: string[] = [];
for (let slotName in ast.slots) {
@@ -1316,7 +1316,7 @@ export class CodeGenerator {
if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx, this)`);
this.define(ctxStr, `capture(ctx)`);
}
let id = generateId("comp");
this.staticDefs.push({
+1 -1
View File
@@ -25,7 +25,7 @@ export function makeRootFiber(node: ComponentNode): Fiber {
current.children = [];
current.childrenMap = {};
current.bdom = null;
if (fibersInError.has(root)) {
if (fibersInError.has(current)) {
fibersInError.delete(current);
fibersInError.delete(root);
current.appliedToDom = false;
+1 -1
View File
@@ -28,7 +28,7 @@ const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]);
* @returns the raw type of the object
*/
function rawType(obj: any) {
return objectToString.call(obj).slice(8, -1);
return objectToString.call(toRaw(obj)).slice(8, -1);
}
/**
* Checks whether a given value can be made into a reactive object.
+10 -3
View File
@@ -29,6 +29,13 @@ function callSlot(
const slots = ctx.props.slots || {};
const { __render, __ctx, __scope } = slots[name] || {};
const slotScope = ObjectCreate(__ctx || {});
if (defaultContent) {
Object.defineProperty(slotScope, helpers.zero, {
get() {
return defaultContent(ctx, parent, key);
},
});
}
if (__scope) {
slotScope[__scope] = extra;
}
@@ -46,8 +53,8 @@ function callSlot(
return slotBDom || text("");
}
function capture(ctx: any, component: any): any {
const result = ObjectCreate(component);
function capture(ctx: any): any {
const result = ObjectCreate(ctx);
for (let k in ctx) {
result[k] = ctx[k];
}
@@ -114,7 +121,7 @@ class LazyValue {
constructor(fn: any, ctx: any, component: any, node: any, key: any) {
this.fn = fn;
this.ctx = capture(ctx, component);
this.ctx = capture(ctx);
this.component = component;
this.node = node;
this.key = key;
@@ -941,7 +941,7 @@ exports[`can catch errors catching error, rethrow, render parent -- a main comp
ctx[\`cp\`] = v_block1[i1];
const key1 = ctx['cp'].id;
const v1 = ctx['cp'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp2({onError: ()=>this.cleanUp(v1.id),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1);
}
return list(c_block1);
@@ -1019,7 +1019,7 @@ exports[`can catch errors catching in child makes parent render 1`] = `
ctx[\`elem\`] = v_block1[i1];
const key1 = ctx['elem'][0];
const v1 = ctx['elem'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp2({onError: (_error)=>this.onError(v1[0],_error),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1);
}
return list(c_block1);
@@ -1195,131 +1195,6 @@ exports[`can catch errors onError in class inheritance is not called if no rethr
}"
`;
exports[`can catch errors re-render parent when self is in error - 2 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Classic\`, true, false, false, false);
const comp2 = app.createComponent(\`Classic\`, true, false, false, false);
return function template(ctx, node, key = \\"\\") {
const b2 = comp1({hasBoom: true,state: ctx['reactive']}, key + \`__1\`, node, this, null);
const b3 = comp2({hasBoom: false,state: ctx['reactive']}, key + \`__2\`, node, this, null);
return multi([b2, b3]);
}
}"
`;
exports[`can catch errors re-render parent when self is in error - 2 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`BoomWrapper\`, true, false, false, false);
let block3 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2,b3;
if (ctx['props'].hasBoom) {
b2 = comp1({state: ctx['props'].state}, key + \`__1\`, node, this, null);
} else {
let txt1 = ctx['props'].state.safeTree;
b3 = block3([txt1]);
}
return multi([b2, b3]);
}
}"
`;
exports[`can catch errors re-render parent when self is in error - 2 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Boom\`, true, false, false, true);
return function template(ctx, node, key = \\"\\") {
let b2,b3;
if (ctx['state'].errorTree==='error') {
b2 = comp1({}, key + \`__1\`, node, this, null);
} else {
b3 = text(ctx['state'].errorTree);
}
return multi([b2, b3]);
}
}"
`;
exports[`can catch errors re-render parent when self is in error - 2 4`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div/>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`can catch errors re-render parent when self is in error 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Classic\`, true, false, false, true);
return function template(ctx, node, key = \\"\\") {
return comp1({}, key + \`__1\`, node, this, null);
}
}"
`;
exports[`can catch errors re-render parent when self is in error 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { bind } = helpers;
const comp1 = app.createComponent(\`BoomWrapper\`, true, false, false, false);
let block3 = createBlock(\`<div/>\`);
return function template(ctx, node, key = \\"\\") {
let b2,b3;
if (!ctx['inError']) {
b2 = comp1({onError: bind(this, ctx['onErrorAsProps'])}, key + \`__1\`, node, this, null);
} else {
b3 = block3();
}
return multi([b2, b3]);
}
}"
`;
exports[`can catch errors re-render parent when self is in error 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Boom\`, true, false, false, true);
return function template(ctx, node, key = \\"\\") {
return comp1({}, key + \`__1\`, node, this, null);
}
}"
`;
exports[`can catch errors re-render parent when self is in error 4`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div/>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`errors and promises a rendering error in a sub component will reject the mount promise 1`] = `
"function anonymous(app, bdom, helpers
) {
@@ -78,7 +78,7 @@ exports[`refs refs are properly bound in slots 1`] = `
return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['state'].val;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([txt1], [b3]);
}
@@ -54,7 +54,7 @@ exports[`slots can define and call slots 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b4 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1}, 'footer': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b4]);
}
@@ -89,7 +89,7 @@ exports[`slots can define and call slots with bound params 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'abc': {__render: slot1.bind(this), __ctx: ctx1, getValue: bind(this, ctx['getValue'])}})}, key + \`__1\`, node, this, null);
}
}"
@@ -129,7 +129,7 @@ exports[`slots can define and call slots with params 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b4 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1, param: ctx['var']}, 'footer': {__render: slot2.bind(this), __ctx: ctx1, param: '5'}})}, key + \`__1\`, node, this, null);
return block1([], [b4]);
}
@@ -349,6 +349,42 @@ exports[`slots content is the default slot 2`] = `
}"
`;
exports[`slots default content is available as 0 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { zero, markRaw } = helpers;
const comp1 = app.createComponent(\`Dialog\`, true, true, false, true);
function slot1(ctx, node, key = \\"\\") {
const b2 = text(\`Before - \`);
const b3 = ctx[zero];
const b4 = text(\` - After\`);
return multi([b2, b3, b4]);
}
return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null);
}
}"
`;
exports[`slots default content is available as 0 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { callSlot } = helpers;
function defaultContent1(ctx, node, key = \\"\\") {
return text(\`default content\`);
}
return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {}, defaultContent1.bind(this));
}
}"
`;
exports[`slots default content is not rendered if named slot is provided 1`] = `
"function anonymous(app, bdom, helpers
) {
@@ -363,7 +399,7 @@ exports[`slots default content is not rendered if named slot is provided 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -442,7 +478,7 @@ exports[`slots default slot next to named slot, with default content 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -619,7 +655,7 @@ exports[`slots dynamic slot in multiple locations 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({location: ctx['state'].location,slots: markRaw({'coffee': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -685,7 +721,7 @@ exports[`slots dynamic t-slot call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({slots: markRaw({'slot1': {__render: slot1.bind(this), __ctx: ctx1}, 'slot2': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b6]);
}
@@ -732,7 +768,7 @@ exports[`slots dynamic t-slot call with default 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({slots: markRaw({'slot1': {__render: slot1.bind(this), __ctx: ctx1}, 'slot2': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b6]);
}
@@ -865,7 +901,7 @@ exports[`slots mix of slots, t-call, t-call with body, and giving own props chil
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -991,7 +1027,7 @@ exports[`slots multiple roots are allowed in a named slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp1({slots: markRaw({'content': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b5]);
}
@@ -1031,7 +1067,7 @@ exports[`slots multiple slots containing components 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp3({slots: markRaw({'s1': {__render: slot1.bind(this), __ctx: ctx1}, 's2': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__3\`, node, this, null);
}
}"
@@ -1085,7 +1121,7 @@ exports[`slots named slot inside slot 1`] = `
}
function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx, this);
const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null);
}
@@ -1095,7 +1131,7 @@ exports[`slots named slot inside slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp2({slots: markRaw({'brol': {__render: slot1.bind(this), __ctx: ctx1}, 'default': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b5]);
}
@@ -1136,7 +1172,7 @@ exports[`slots named slot inside slot, part 3 1`] = `
}
function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx, this);
const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null);
}
@@ -1146,7 +1182,7 @@ exports[`slots named slot inside slot, part 3 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp2({slots: markRaw({'brol': {__render: slot1.bind(this), __ctx: ctx1}, 'default': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b5]);
}
@@ -1221,7 +1257,7 @@ exports[`slots named slots inside slot, again 1`] = `
}
function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx, this);
const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol2': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null);
}
@@ -1231,7 +1267,7 @@ exports[`slots named slots inside slot, again 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp2({slots: markRaw({'brol1': {__render: slot1.bind(this), __ctx: ctx1}, 'default': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b5]);
}
@@ -1569,7 +1605,7 @@ exports[`slots simple dynamic slot with slot scope 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1599,7 +1635,7 @@ exports[`slots simple named and empty slot -- 2 1`] = `
const comp1 = app.createComponent(\`Child\`, true, true, false, true);
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'myEmptySlot': {myProp: 'myProp text'}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1636,7 +1672,7 @@ exports[`slots simple named and empty slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'myEmptySlot': {}, 'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1676,7 +1712,7 @@ exports[`slots simple slot with slot scope 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1712,7 +1748,7 @@ exports[`slots slot and (inline) t-call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -1762,7 +1798,7 @@ exports[`slots slot and t-call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -1997,7 +2033,7 @@ exports[`slots slot content is bound to caller (variation) 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
}
}"
@@ -2224,7 +2260,7 @@ exports[`slots slot preserves properly parented relationship, even through t-cal
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -2285,7 +2321,7 @@ exports[`slots slot with slot scope and t-props 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"info\\"}})}, key + \`__1\`, node, this, null);
}
}"
@@ -2387,7 +2423,7 @@ exports[`slots slots are rendered with proper context 1`] = `
return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['state'].val;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([txt1], [b3]);
}
@@ -2431,7 +2467,7 @@ exports[`slots slots are rendered with proper context, part 2 1`] = `
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`user\`] = v_block2[i1];
const key1 = ctx['user'].id;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b7 = comp1({to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null);
c_block2[i1] = withKey(block3([], [b7]), key1);
}
@@ -2480,7 +2516,7 @@ exports[`slots slots are rendered with proper context, part 3 1`] = `
ctx[\`user\`] = v_block2[i1];
const key1 = ctx['user'].id;
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['user'].name);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp1({to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null);
c_block2[i1] = withKey(block3([], [b5]), key1);
}
@@ -2523,7 +2559,7 @@ exports[`slots slots are rendered with proper context, part 4 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['state'].user.name);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({to: '/user/'+ctx['state'].user.id,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2565,7 +2601,7 @@ exports[`slots slots in slots, with vars 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"test\\", ctx['state'].name);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2627,7 +2663,7 @@ exports[`slots slots in t-foreach and re-rendering 1`] = `
ctx[\`n\`] = v_block2[i1];
ctx[\`n_index\`] = i1;
const key1 = ctx['n_index'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block2[i1] = withKey(comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null), key1);
}
const b2 = list(c_block2);
@@ -2682,7 +2718,7 @@ exports[`slots slots in t-foreach in t-foreach 1`] = `
for (let i2 = 0; i2 < l_block6; i2++) {
ctx[\`node2\`] = v_block6[i2];
const key2 = ctx['node2'].key;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block6[i2] = withKey(comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}__\${key2}\`, node, this, null), key2);
}
ctx = ctx.__proto__;
@@ -2734,7 +2770,7 @@ exports[`slots slots in t-foreach with t-set and re-rendering 1`] = `
ctx[\`n_index\`] = i1;
const key1 = ctx['n_index'];
setContextValue(ctx, \\"dummy\\", ctx['n_index']);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block2[i1] = withKey(comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null), key1);
}
const b2 = list(c_block2);
@@ -2774,7 +2810,7 @@ exports[`slots t-debug on a t-set-slot (defining a slot) 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'content': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2813,7 +2849,7 @@ exports[`slots t-set t-value in a slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2847,7 +2883,7 @@ exports[`slots t-set-slot=default has priority over rest of the content 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
}
}"
@@ -2905,7 +2941,7 @@ exports[`slots t-slot in recursive templates 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -3083,7 +3119,7 @@ exports[`slots t-slot within dynamic t-call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -558,7 +558,7 @@ exports[`t-call t-call-context: ComponentNode is not looked up in the context 2`
const refs = this.__owl__.refs;
const ref1 = (el) => refs[\`myRef\`] = el;
const b2 = block2([ref1]);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b7 = comp1({prop: bind(this, ctx['method']),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return multi([b2, b7]);
}
@@ -576,3 +576,51 @@ exports[`t-call t-call-context: ComponentNode is not looked up in the context 3`
}
}"
`;
exports[`t-call t-call-context: slots don't make component available again when context is captured 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`template\`);
return function template(ctx, node, key = \\"\\") {
let ctx1 = {};
return callTemplate_1.call(this, ctx1, node, key + \`__1\`);
}
}"
`;
exports[`t-call t-call-context: slots don't make component available again when context is captured 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { isBoundary, withDefault, setContextValue, capture, markRaw } = helpers;
const comp1 = app.createComponent(\`Child\`, true, true, false, true);
function slot1(ctx, node, key = \\"\\") {
return text(ctx['someValue']);
}
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"dummy\\", 0);
const ctx1 = capture(ctx);
const props1 = {slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})};
helpers.validateProps(\`Child\`, props1, this);
return comp1(props1, key + \`__1\`, node, this, null);
}
}"
`;
exports[`t-call t-call-context: slots don't make component available again when context is captured 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { callSlot } = helpers;
return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {});
}
}"
`;
@@ -175,7 +175,7 @@ exports[`list of components order is correct when slots are not of same type 1`]
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'a': {__render: slot1.bind(this), __ctx: ctx1, active: !ctx['state'].active}, 'b': {__render: slot2.bind(this), __ctx: ctx1, active: true}, 'c': {__render: slot3.bind(this), __ctx: ctx1, active: ctx['state'].active}})}, key + \`__1\`, node, this, null);
}
}"
@@ -401,7 +401,7 @@ exports[`t-on t-on on t-set-slots 1`] = `
const b2 = text(\` [\`);
const b3 = text(ctx['state'].count);
const b4 = text(\`] \`);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b8 = comp1({slots: markRaw({'myslot': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return multi([b2, b3, b4, b8]);
}
@@ -21,7 +21,7 @@ exports[`t-set slot setted value (with t-set) not accessible with t-esc 1`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"iter\\", 'source');
let txt1 = ctx['iter'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b2 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
let txt2 = ctx['iter'];
return block1([txt1, txt2], [b2]);
@@ -70,7 +70,7 @@ exports[`t-set slots with a t-set with a component in body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -127,7 +127,7 @@ exports[`t-set slots with an t-set with a component in body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -178,7 +178,7 @@ exports[`t-set slots with an unused t-set with a component in body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
-126
View File
@@ -9,7 +9,6 @@ import {
onRendered,
onWillUnmount,
useState,
reactive,
xml,
} from "../../src/index";
import {
@@ -786,131 +785,6 @@ describe("can catch errors", () => {
expect(mockConsoleWarn).toBeCalledTimes(0);
});
test("re-render parent when self is in error", async () => {
class Boom extends Component {
static template = xml`<div />`;
setup() {
onWillStart(() => {
throw new Error("Boom Error");
});
}
}
const steps: string[] = [];
class BoomWrapper extends Component {
static template = xml`<Boom />`;
static components = { Boom };
setup() {
onError(() => {
steps.push("onError in child");
this.props.onError();
});
}
}
class Classic extends Component {
static template = xml`<BoomWrapper t-if="!inError" onError.bind="onErrorAsProps"/><div t-else="" />`;
static components = { BoomWrapper };
inError: Boolean = false;
setup() {
onMounted(() => {
steps.push("mounted");
});
}
onErrorAsProps() {
this.inError = true;
this.render(true);
}
}
class App extends Component {
static template = xml`<Classic />`;
static components = { Classic };
}
await mount(App, fixture);
expect(steps).toEqual(["onError in child", "mounted"]);
});
test("re-render parent when self is in error - 2", async () => {
class Boom extends Component {
static template = xml`<div />`;
setup() {
onWillStart(() => {
throw new Error("Boom Error");
});
}
}
const steps: string[] = [];
class BoomWrapper extends Component {
static template = xml`<Boom t-if="state.errorTree === 'error'" /><t t-else="" t-esc="state.errorTree" />`;
static components = { Boom };
state: any;
setup() {
this.state = useState(this.props.state);
onError(() => {
steps.push("onError");
this.state.onError();
});
onWillRender(() => {
steps.push(`BoomWrapper willRender`);
});
}
}
let classicId = 0;
class Classic extends Component {
static template = xml`
<BoomWrapper t-if="props.hasBoom" state="props.state"/>
<div t-else="" t-esc="props.state.safeTree" />
`;
static components = { BoomWrapper };
id: Number = 0;
state: any;
inError: Boolean = false;
setup() {
this.id = classicId++;
onMounted(() => {
steps.push(`mounted ${this.id}`);
});
onWillRender(() => {
steps.push(`Classic willRender ${this.id}`);
});
}
}
class App extends Component {
reactive: any;
setup() {
this.reactive = reactive({
errorTree: "error",
safeTree: "safe",
onError() {
this.safeTree = "safe2";
this.errorTree = "errorHandled";
},
});
}
static template = xml`<Classic hasBoom="true" state="reactive" /><Classic hasBoom="false" state="reactive"/>`;
static components = { Classic };
}
await mount(App, fixture);
expect(steps).toEqual([
"Classic willRender 0",
"Classic willRender 1",
"BoomWrapper willRender",
"onError",
"Classic willRender 1",
"BoomWrapper willRender",
"mounted 1",
"mounted 0",
]);
expect(fixture.innerHTML).toBe("errorHandled<div>safe2</div>");
});
test("can catch an error in the willStart call", async () => {
class ErrorComponent extends Component {
static template = xml`<div>Some text</div>`;
+13
View File
@@ -1933,4 +1933,17 @@ describe("slots", () => {
"<p>1 hello <div>child</div></p><p>2 hello <div>child</div></p>"
);
});
test("default content is available as 0", async () => {
class Dialog extends Component {
static template = xml`<t t-slot="default">default content</t>`;
}
class Parent extends Component {
static template = xml`<Dialog>Before - <t t-out="0"/> - After</Dialog>`;
static components = { Dialog };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("Before - default content - After");
});
});
+26
View File
@@ -357,4 +357,30 @@ describe("t-call", () => {
expect(Object.keys(child.__owl__.refs)).toEqual([]);
expect(Object.keys(root.__owl__.refs)).toEqual(["myRef", "myRef2"]);
});
test("t-call-context: slots don't make component available again when context is captured", async () => {
class Child extends Component {
static template = xml`<t t-slot="default"/>`;
}
class Root extends Component {
static template = xml`<t t-call="template" t-call-context="{}"/>`;
static components = { Child };
someValue = "Hello";
}
await mount(Root, fixture, {
test: true,
templates: `
<templates>
<t t-name="template">
<t t-set="dummy" t-value="0"/>
<Child>
<t t-esc="someValue"/>
</Child>
</t>
</templates>`,
});
expect(fixture.innerHTML).toBe("");
});
});
+7 -7
View File
@@ -20,7 +20,7 @@ exports[`Portal Add and remove portals 1`] = `
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
}
return list(c_block1);
@@ -49,7 +49,7 @@ exports[`Portal Add and remove portals on div 1`] = `
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
}
return list(c_block1);
@@ -80,7 +80,7 @@ exports[`Portal Add and remove portals with t-foreach 1`] = `
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
let txt1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block1[i1] = withKey(block2([txt1], [b6]), key1);
}
@@ -112,7 +112,7 @@ exports[`Portal Add and remove portals with t-foreach and destroy 1`] = `
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
let txt1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block1[i1] = withKey(block2([txt1], [b6]), key1);
}
@@ -145,7 +145,7 @@ exports[`Portal Add and remove portals with t-foreach inside div 1`] = `
ctx[\`portalId\`] = v_block2[i1];
const key1 = ctx['portalId'];
let txt1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b7 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block2[i1] = withKey(block3([txt1], [b7]), key1);
}
@@ -431,7 +431,7 @@ exports[`Portal conditional use of Portal with child and div 2`] = `
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`elem\`] = v_block2[i1];
const key1 = ctx['elem'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block2[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
}
const b2 = list(c_block2);
@@ -481,7 +481,7 @@ exports[`Portal conditional use of Portal with child and div, variation 2`] = `
for (let i1 = 0; i1 < l_block3; i1++) {
ctx[\`elem\`] = v_block3[i1];
const key1 = ctx['elem'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block3[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
}
const b3 = list(c_block3);
+27 -1
View File
@@ -9,7 +9,7 @@ import {
markRaw,
toRaw,
} from "../src";
import { reactive } from "../src/runtime/reactivity";
import { reactive, getSubscriptions } from "../src/runtime/reactivity";
import { batched } from "../src/runtime/utils";
import {
makeDeferred,
@@ -1020,6 +1020,32 @@ describe("Reactivity", () => {
expect(n3).toBe(2);
});
test("reactive inside other: reading the inner reactive from outer doesn't affect the inner's subscriptions", async () => {
const getObservedKeys = (obj: any) => getSubscriptions(obj).flatMap(({ keys }) => keys);
let n1 = 0;
let n2 = 0;
const innerCb = () => n1++;
const outerCb = () => n2++;
const inner = createReactive({ a: 1 }, innerCb);
const outer = createReactive({ b: inner }, outerCb);
expect(n1).toBe(0);
expect(n2).toBe(0);
expect(getObservedKeys(innerCb)).toEqual([]);
expect(getObservedKeys(outerCb)).toEqual([]);
outer.b.a;
expect(getObservedKeys(innerCb)).toEqual([]);
expect(getObservedKeys(outerCb)).toEqual(["b", "a"]);
expect(n1).toBe(0);
expect(n2).toBe(0);
outer.b.a = 2;
expect(getObservedKeys(innerCb)).toEqual([]);
expect(getObservedKeys(outerCb)).toEqual([]);
expect(n1).toBe(0);
expect(n2).toBe(1);
});
// test("notification is not done after unregistration", async () => {
// let n = 0;
// const observer = () => n++;