如何在 svelte 中创建动态树结构?
How to create dynamic tree structure in svelte?
我需要一个 svelte 树视图,每个节点都有复选框。
我无法获得这样的功能:如果我们检查一个父节点,那么它的所有(只有它的)子节点是否是文件夹的文件,所有这些(子)自动检查,并且稍后取消选中其中任何一个,那么父项也应该取消选中。我怎样才能做到这一点?
下面是我从 here 中遵循的代码。我尝试了一些东西但没有运气。
App.svelte-
<script>
import Folder from './Folder.svelte';
let root = [
{
name: 'Important work stuff',
files: [
{ name: 'quarterly-results.xlsx' }
]
},
{
name: 'Animal GIFs',
files: [
{
name: 'Dogs',
files: [
{ name: 'treadmill.gif' },
{ name: 'rope-jumping.gif' }
]
},
{
name: 'Goats',
files: [
{ name: 'parkour.gif' },
{ name: 'rampage.gif' }
]
},
{ name: 'cat-roomba.gif' },
{ name: 'duck-shuffle.gif' },
{ name: 'monkey-on-a-pig.gif' }
]
},
{ name: 'TODO.md' }
];
</script>
<Folder name="Home" files={root} expanded/>
File.svelte-
<script>
export let name;
$: type = name.slice(name.lastIndexOf('.') + 1);
</script>
<span style="background-image: url(tutorial/icons/{type}.svg)">{name}</span>
<style>
span {
padding: 0 0 0 1.5em;
background: 0 0.1em no-repeat;
background-size: 1em 1em;
}
</style>
Folder.svelte-
<script>
import File from './File.svelte';
export let expanded = false;
export let name;
export let files;
let checkedState = true;
function toggle() {
expanded = !expanded;
}
function onCheckboxChanged(e){
console.log("out: "+document.getElementById("cb1").checked)
}
</script>
<input type="checkbox" on:change={onCheckboxChanged} id="cb1" bind:checked={checkedState}><span class:expanded on:click={toggle}>{name}</span>
{#if expanded}
<ul>
{#each files as file}
<li>
{#if file.files}
<svelte:self {...file}/>
{:else}
<input type="checkbox" on:change={onCheckboxChanged} id="cb1" bind:checked={checkedState}><File {...file}/>
{/if}
</li>
{/each}
</ul>
{/if}
<style>
span {
padding: 0 0 0 1.5em;
background: url(tutorial/icons/folder.svg) 0 0.1em no-repeat;
background-size: 1em 1em;
font-weight: bold;
cursor: pointer;
}
.expanded {
background-image: url(tutorial/icons/folder-open.svg);
}
ul {
padding: 0.2em 0 0 0.5em;
margin: 0 0 0 0.5em;
list-style: none;
border-left: 1px solid #eee;
}
li {
padding: 0.2em 0;
}
</style>
- 删除文件组件旁边的复选框中的绑定。这将防止您单击文件的复选框并检查整个文件夹的情况。相反,您只需要在检查 Folder 时将状态传递给子 File 组件。所以像这样更改代码:
<!-- <input type="checkbox" bind:checked={checkedState}> -->
<input type="checkbox" checked={checkedState}>
- 然后你需要将文件夹的选中状态传递给所有子文件夹组件(所以当你选中文件夹时,里面的文件夹也会被选中)。你可以通过使
checkedState
成为一个道具,而不仅仅是常规变量,然后在调用 svelte:self
组件时传递这个道具
<script>
// let checkedState = true;
export let checkedState = true;
</script>
<!-- <svelte:self {...file}/> -->
<svelte:self {...file} {checkedState} />
- 之后只剩下一个问题案例。当所有子组件都被(未)选中时,父组件也应该被(未)选中。我真的看不到用当前结构实现这个东西的可能性。我建议您在文件组件中移动复选框,然后在文件对象(在
App.svelte
中)中创建字段 checked
或 selected
,这样您就可以轻松管理任何子组件中的所有状态。从根文件夹组件中提取数据时也更容易读取此值(例如,如果您需要将此数据发送到服务器)。添加道具后,您当然应该使用对象属性,而不是组件的状态。祝你好运!
P.S:不要在组件中使用静态 ID,在每个循环中更是如此。这将使您的输出 html 无效。
当我尝试构建与您尝试构建的相同的 TreeView 组件时,我遇到了这个问题。
我用 compnent event.
实现了你想要的 dynamic
功能
这是demo, which also use svelte:self
and inspired by this REPL
这种方法的要点是:
- 子组件没有责任计算树的状态,它只需要表示它。
- 当子节点的状态发生变化时,让父组件进行整个树的状态计算。
我需要一个 svelte 树视图,每个节点都有复选框。
我无法获得这样的功能:如果我们检查一个父节点,那么它的所有(只有它的)子节点是否是文件夹的文件,所有这些(子)自动检查,并且稍后取消选中其中任何一个,那么父项也应该取消选中。我怎样才能做到这一点?
下面是我从 here 中遵循的代码。我尝试了一些东西但没有运气。
App.svelte-
<script>
import Folder from './Folder.svelte';
let root = [
{
name: 'Important work stuff',
files: [
{ name: 'quarterly-results.xlsx' }
]
},
{
name: 'Animal GIFs',
files: [
{
name: 'Dogs',
files: [
{ name: 'treadmill.gif' },
{ name: 'rope-jumping.gif' }
]
},
{
name: 'Goats',
files: [
{ name: 'parkour.gif' },
{ name: 'rampage.gif' }
]
},
{ name: 'cat-roomba.gif' },
{ name: 'duck-shuffle.gif' },
{ name: 'monkey-on-a-pig.gif' }
]
},
{ name: 'TODO.md' }
];
</script>
<Folder name="Home" files={root} expanded/>
File.svelte-
<script>
export let name;
$: type = name.slice(name.lastIndexOf('.') + 1);
</script>
<span style="background-image: url(tutorial/icons/{type}.svg)">{name}</span>
<style>
span {
padding: 0 0 0 1.5em;
background: 0 0.1em no-repeat;
background-size: 1em 1em;
}
</style>
Folder.svelte-
<script>
import File from './File.svelte';
export let expanded = false;
export let name;
export let files;
let checkedState = true;
function toggle() {
expanded = !expanded;
}
function onCheckboxChanged(e){
console.log("out: "+document.getElementById("cb1").checked)
}
</script>
<input type="checkbox" on:change={onCheckboxChanged} id="cb1" bind:checked={checkedState}><span class:expanded on:click={toggle}>{name}</span>
{#if expanded}
<ul>
{#each files as file}
<li>
{#if file.files}
<svelte:self {...file}/>
{:else}
<input type="checkbox" on:change={onCheckboxChanged} id="cb1" bind:checked={checkedState}><File {...file}/>
{/if}
</li>
{/each}
</ul>
{/if}
<style>
span {
padding: 0 0 0 1.5em;
background: url(tutorial/icons/folder.svg) 0 0.1em no-repeat;
background-size: 1em 1em;
font-weight: bold;
cursor: pointer;
}
.expanded {
background-image: url(tutorial/icons/folder-open.svg);
}
ul {
padding: 0.2em 0 0 0.5em;
margin: 0 0 0 0.5em;
list-style: none;
border-left: 1px solid #eee;
}
li {
padding: 0.2em 0;
}
</style>
- 删除文件组件旁边的复选框中的绑定。这将防止您单击文件的复选框并检查整个文件夹的情况。相反,您只需要在检查 Folder 时将状态传递给子 File 组件。所以像这样更改代码:
<!-- <input type="checkbox" bind:checked={checkedState}> -->
<input type="checkbox" checked={checkedState}>
- 然后你需要将文件夹的选中状态传递给所有子文件夹组件(所以当你选中文件夹时,里面的文件夹也会被选中)。你可以通过使
checkedState
成为一个道具,而不仅仅是常规变量,然后在调用svelte:self
组件时传递这个道具
<script>
// let checkedState = true;
export let checkedState = true;
</script>
<!-- <svelte:self {...file}/> -->
<svelte:self {...file} {checkedState} />
- 之后只剩下一个问题案例。当所有子组件都被(未)选中时,父组件也应该被(未)选中。我真的看不到用当前结构实现这个东西的可能性。我建议您在文件组件中移动复选框,然后在文件对象(在
App.svelte
中)中创建字段checked
或selected
,这样您就可以轻松管理任何子组件中的所有状态。从根文件夹组件中提取数据时也更容易读取此值(例如,如果您需要将此数据发送到服务器)。添加道具后,您当然应该使用对象属性,而不是组件的状态。祝你好运!
P.S:不要在组件中使用静态 ID,在每个循环中更是如此。这将使您的输出 html 无效。
当我尝试构建与您尝试构建的相同的 TreeView 组件时,我遇到了这个问题。 我用 compnent event.
实现了你想要的dynamic
功能
这是demo, which also use svelte:self
and inspired by this REPL
这种方法的要点是:
- 子组件没有责任计算树的状态,它只需要表示它。
- 当子节点的状态发生变化时,让父组件进行整个树的状态计算。