fix checkbox bind:group when index depends on another context (#5835)

pull/5849/head
Tan Li Hau 4 years ago committed by GitHub
parent 2d697a38c5
commit 662d9b44e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,6 +2,7 @@
## Unreleased
* Fix checkbox `bind:group` in nested `{#each}` contexts ([#5811](https://github.com/sveltejs/svelte/issues/5811))
* Add graphics roles as known ARIA roles ([#5822](https://github.com/sveltejs/svelte/pull/5822))
## 3.31.0

@ -39,6 +39,7 @@ export default class Block {
dependencies: Set<string> = new Set();
bindings: Map<string, Bindings>;
binding_group_initialised: Set<string> = new Set();
chunks: {
declarations: Array<Node | Node[]>;

@ -32,7 +32,7 @@ export default class Renderer {
blocks: Array<Block | Node | Node[]> = [];
readonly: Set<string> = new Set();
meta_bindings: Array<Node | Node[]> = []; // initial values for e.g. window.innerWidth, if there's a <svelte:window> meta tag
binding_groups: Map<string, { binding_group: (to_reference?: boolean) => Node; is_context: boolean; contexts: string[]; index: number }> = new Map();
binding_groups: Map<string, { binding_group: (to_reference?: boolean) => Node; is_context: boolean; contexts: string[]; index: number; keypath: string }> = new Map();
block: Block;
fragment: FragmentWrapper;

@ -116,11 +116,11 @@ export default class BindingWrapper {
switch (this.node.name) {
case 'group':
{
const { binding_group, is_context, contexts, index } = get_binding_group(parent.renderer, this.node, block);
const { binding_group, is_context, contexts, index, keypath } = get_binding_group(parent.renderer, this.node, block);
block.renderer.add_to_context('$$binding_groups');
if (is_context) {
if (is_context && !block.binding_group_initialised.has(keypath)) {
if (contexts.length > 1) {
let binding_group = x`${block.renderer.reference('$$binding_groups')}[${index}]`;
for (const name of contexts.slice(0, -1)) {
@ -133,6 +133,7 @@ export default class BindingWrapper {
block.chunks.init.push(
b`${binding_group(true)} = [];`
);
block.binding_group_initialised.add(keypath);
}
block.chunks.hydrate.push(
@ -257,8 +258,22 @@ function get_binding_group(renderer: Renderer, value: Binding, block: Block) {
let keypath = parts.join('.');
const contexts = [];
const contextual_dependencies = new Set<string>();
const { template_scope } = value.expression;
const add_contextual_dependency = (dep: string) => {
contextual_dependencies.add(dep);
const owner = template_scope.get_owner(dep);
if (owner.type === 'EachBlock') {
for (const dep of owner.expression.contextual_dependencies) {
add_contextual_dependency(dep);
}
}
};
for (const dep of value.expression.contextual_dependencies) {
add_contextual_dependency(dep);
}
for (const dep of contextual_dependencies) {
const context = block.bindings.get(dep);
let key;
let name;
@ -302,7 +317,8 @@ function get_binding_group(renderer: Renderer, value: Binding, block: Block) {
},
is_context: contexts.length > 0,
contexts,
index
index,
keypath
});
}

@ -17,10 +17,18 @@ export default function mark_each_block_bindings(
});
if (binding.name === 'group') {
const add_index_binding = (name: string) => {
const each_block = parent.node.scope.get_owner(name);
if (each_block.type === 'EachBlock') {
each_block.has_index_binding = true;
for (const dep of each_block.expression.contextual_dependencies) {
add_index_binding(dep);
}
}
};
// for `<input bind:group={} >`, we make sure that all the each blocks creates context with `index`
for (const name of binding.expression.contextual_dependencies) {
const each_block = parent.node.scope.get_owner(name);
(each_block as EachBlock).has_index_binding = true;
add_index_binding(name);
}
}
}

@ -0,0 +1,53 @@
export default {
html: `
<input type="checkbox" value="a" data-index="x-1">
<input type="checkbox" value="b" data-index="x-1">
<input type="checkbox" value="c" data-index="x-1">
<input type="checkbox" value="a" data-index="x-2">
<input type="checkbox" value="b" data-index="x-2">
<input type="checkbox" value="c" data-index="x-2">
<input type="checkbox" value="a" data-index="y-1">
<input type="checkbox" value="b" data-index="y-1">
<input type="checkbox" value="c" data-index="y-1">
<input type="checkbox" value="a" data-index="y-2">
<input type="checkbox" value="b" data-index="y-2">
<input type="checkbox" value="c" data-index="y-2">
<input type="checkbox" value="a" data-index="z-1">
<input type="checkbox" value="b" data-index="z-1">
<input type="checkbox" value="c" data-index="z-1">
<input type="checkbox" value="a" data-index="z-2">
<input type="checkbox" value="b" data-index="z-2">
<input type="checkbox" value="c" data-index="z-2">
`,
async test({ assert, component, target, window }) {
const inputs = target.querySelectorAll('input');
const checked = new Set();
const checkInbox = async (i) => {
checked.add(i);
inputs[i].checked = true;
await inputs[i].dispatchEvent(event);
};
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
const event = new window.Event('change');
await checkInbox(2);
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
await checkInbox(12);
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
await checkInbox(8);
for (let i = 0; i < 18; i++) {
assert.equal(inputs[i].checked, checked.has(i));
}
}
};

@ -0,0 +1,15 @@
<script>
const list = [
{ id: 'x', data: [{ id: 1, data: [] }, { id: 2, data: [] }] },
{ id: 'y', data: [{ id: 1, data: [] }, { id: 2, data: [] }] },
{ id: 'z', data: [{ id: 1, data: [] }, { id: 2, data: [] }] }
];
</script>
{#each list as { id, data }}
{#each data as item}
<input type="checkbox" bind:group={item.data} value="a" data-index="{id}-{item.id}" />
<input type="checkbox" bind:group={item.data} value="b" data-index="{id}-{item.id}" />
<input type="checkbox" bind:group={item.data} value="c" data-index="{id}-{item.id}" />
{/each}
{/each}
Loading…
Cancel
Save