Skip to content

Commit

Permalink
fix(engine): fixes #663 - unique keys for sloted elements (#664)
Browse files Browse the repository at this point in the history
  • Loading branch information
caridy authored and ekashida committed Sep 22, 2018
1 parent e971f2c commit 00529a9
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 1 deletion.
70 changes: 70 additions & 0 deletions packages/lwc-engine/src/framework/__tests__/vm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,75 @@ describe('vm', () => {
expect(getErrorComponentStack(vm.elm)).toBe('<x-parent>\n\t<x-child>');
});
});
describe('slotting for slowpath', () => {
it('should re-keyed slotted content to avoid reusing elements from default content', () => {
const childHTML = compileTemplate(`<template>
<slot>
<h1>default slot default content</h1>
</slot>
<slot name="foo">
<h2>foo slot default content</h2>
</slot>
</template>`);
class ChildComponent extends LightningElement {
render() {
return childHTML;
}
renderedCallback() {
const h1 = this.template.querySelector('h1');
const h2 = this.template.querySelector('h2');
if (h1) {
h1.setAttribute('def-1', 'internal');
}
if (h2) {
h2.setAttribute('def-2', 'internal');
}
}
}
const parentHTML = compileTemplate(`<template>
<c-child>
<template if:true={h1}>
<h1 slot="">slotted</h1>
</template>
<template if:true={h2}>
<h2 slot="foo"></h2>
</template>
</c-child>
</template>`, {
modules: {
'c-child': ChildComponent
}
});
let parentTemplate;
class Parent extends LightningElement {
constructor() {
super();
this.h1 = false;
this.h2 = false;
parentTemplate = this.template;
}
render() {
return parentHTML;
}
enable() {
this.h1 = this.h2 = true;
}
disable() {
this.h1 = this.h2 = true;
}
}
Parent.track = { h1: 1, h2: 1 };
Parent.publicMethods = ['enable', 'disable'];

const elm = createElement('x-parent', { is: Parent });
document.body.appendChild(elm);
elm.enable();
return Promise.resolve().then(() => {
// at this point, if we are reusing the h1 and h2 from the default content
// of the slots in c-child, they will have an extraneous attribute on them,
// which will be a problem.
expect(parentTemplate.querySelector('c-child').outerHTML).toBe(`<c-child><h1 slot="">slotted</h1><h2 slot="foo"></h2></c-child>`);
});
});
});
});
7 changes: 6 additions & 1 deletion packages/lwc-engine/src/framework/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,12 @@ export function allocateInSlot(vm: VM, children: VNodes) {
const data = (vnode.data as VNodeData);
const slotName = ((data.attrs && data.attrs.slot) || '') as string;
const vnodes: VNodes = cmpSlots[slotName] = cmpSlots[slotName] || [];
vnodes.push(vnode);
// re-keying the vnodes is necessary to avoid conflicts with default content for the slot
// which might have similar keys. Each vnode will always have a key that
// starts with a numeric character from compiler. In this case, we add a unique
// notation for slotted vnodes keys, e.g.: `@foo:1:1`
vnode.key = `@${slotName}:${vnode.key}`;
ArrayPush.call(vnodes, vnode);
}
if (!vm.isDirty) {
// We need to determine if the old allocation is really different from the new one
Expand Down

0 comments on commit 00529a9

Please sign in to comment.