Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
484 views
in Technique[技术] by (71.8m points)

javascript - Svelte.js-无法从列表中正确删除项目(Svelte.js - Can't Properly Delete Item From List)

I am building an application where users can add and delete items of different "types".

(我正在构建一个应用程序,用户可以在其中添加和删除不同“类型”的项目。)

For example there may be a text type where users can edit the text, and an image type where users can drop in an image.

(例如,可能存在用户可以编辑文本的文本类型和用户可以放入图像的图像类型。)

Adding an item to the list will cause the list to update and display all old items along with the new item.

(在列表中添加项目将导致列表更新并显示所有旧项目以及新项目。)

Similarly, deleting an item will remove that item and cause the list to only display the remaining items.

(同样,删除项目将删除该项目,并使列表仅显示其余项目。)

The add functionality works perfectly fine.

(添加功能可以很好地工作。)

The issue comes with deletes when the items are of different types.

(当项目属于不同类型时,删除会带来问题。)

Here is my (simplified) code:

(这是我的(简化)代码:)

// List.svelte

<script>
    import {writable} from "svelte/store";
    import Wrapper from "./Wrapper.svelte";

    const items = [
        {
            type: "text",
            value: "Test 1"
        },
        {
            type: "img",
            value: "https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif"
        }
    ];

    const listStore = writable(items);
</script>

{#each $listStore as item, i}
    <Wrapper item={item} store={listStore} myIndex={i} />
{/each}
// Wrapper.svelte

<script>
    import {onMount} from "svelte";

    export let item;
    export let store;
    export let myIndex;

    let DynamicItem;

    const handleDeleteItem = () => {
        store.update((items) => ([...items.slice(0, myIndex), ...items.slice(myIndex + 1)]))
    }

    onMount(async () => {
        let imported;

        if(item.type === "text") {
            imported = await import("./TextItem.svelte")
        } else {
            imported = await import("./ImgItem.svelte")
        }

        DynamicItem = imported.default;
    })
</script>

<svelte:component this={DynamicItem} data={item} />
<button on:click={handleDeleteItem}>Delete</button>
// TextItem.svelte

<script>
    export let data;
</script>

<div contenteditable="true" bind:textContent={data.value} />
// ImgItem.svelte

<script>
    export let data;
</script>

<img src="{data.value}" width="100px" height="100px" alt="img alt" />

When the user deletes the TextItem from the list by clicking on the first delete button , the ImgItem no longer displays the image and instead only renders the src attribute as text.

(当用户删除TextItem通过点击第一个从列表中删除button ,该ImgItem不再显示图像,而仅呈现src属性为文本。)

When looking at the HTML output, before the delete action it looks like this:

(查看HTML输出时,在执行删除操作之前,它看起来像这样:)

<body>
    <div contenteditable="true">Test 1</div>
    <button>Delete</button>
    <img src="https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif" alt="img alt" width="100px" height="100px">
    <button>Delete</button>
</body>

And afterwards, it looks like this:

(然后,它看起来像这样:)

<body>
    <div contenteditable="true">https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif</div>
    <button>Delete</button>
</body>

So it looks like Svelte is not truly deleting the TextItem but moving the content from the next item into its place.

(因此,看起来Svelte并没有真正删除TextItem而是将内容从下一个项目移到了它的位置。)

How can I fix my code so that items are truly deleted?

(如何修复我的代码,以便真正删除项目?)

The goal should be to produce the following HTML by performing the same action:

(目标应该是通过执行相同的操作来产生以下HTML:)

<body>
    <img src="https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif" alt="img alt" width="100px" height="100px">
    <button>Delete</button>
</body>

Any help is appreciated.

(任何帮助表示赞赏。)

Thank you

(谢谢)

  ask by mrstack999 translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

So it looks like Svelte is not truly deleting the TextItem but moving the content from the next item into its place.

(因此,看起来Svelte并没有真正删除TextItem,而是将内容从下一个项目移到了它的位置。)

Yes, absolutely, this is what is happening.

(是的,绝对是这样。)

Svelte (like React) only recreate an element in a given position when its nature has obviously changed, for example if the tag name is not the same.

(Svelte(像React一样)仅在其性质明显改变(例如,标记名称不同)时才在给定位置重新创建元素。)

Otherwise, it is considered that it is the same element / component with just some props / state that has changed.

(否则,可以认为它是相同的元素/组件,只是某些道具/状态发生了变化。)

In order to hint the framework that the identity of an element has changed, you must the key functionality.

(为了提示框架元素的身份已更改,您必须具有key功能。)

In React, it's a literal key prop.

(在React中,它是字面意义上的key道具。)

In Svelte, keys are only supported in {#each ...} blocks ( for now? ).

(在Svelte中,键仅在{#each ...}块中受支持( 现在是吗? )。)

The syntax is the following ( docs ):

(语法如下( docs ):)

{#each expression as name, index (key)}...{/each}

# also works, I think it is an oversight in the docs currently
{#each expression as name, (key)}...{/each}

The value of the key must be unique to each item for it to work.

(key的值对于每个项目都必须唯一,才能起作用。)

Typically you'd use an id of sort.

(通常,您会使用排序ID。)

But truly, it can be anything.

(但实际上,它可以是任何东西。)

If the value change for the element / component at a given position, then it will be recreated.

(如果元素/组件在给定位置的值更改,则将重新创建它。)

So, in your case, you could fix your issue by using the item type as a key, for example:

(因此,根据您的情况,可以通过使用项目类型作为键来解决问题,例如:)

{#each $listStore as item, i (item.type)}
    <Wrapper item={item} store={listStore} myIndex={i} />
{/each}

This fixes the issue you're asking about, but it's not such a great idea I must say.

(这可以解决您要询问的问题,但是我必须说这不是一个好主意。)

For other reasons, you'd better add real unique ids to your items and use them instead.

(出于其他原因,您最好将真实的唯一ID添加到商品中,而改用它们。)

With the above code, items of the same type will continue to share DOM elements, and that's probably not what you want.

(使用上面的代码,相同类型的项目将继续共享DOM元素,而这可能并不是您想要的。)

You may have issues with focus or internal state (value) of DOM elements like inputs, for example.

(例如,您可能对DOM元素(如输入)的焦点或内部状态(值)有疑问。)

So, something like this is better and is probably the way to go:

(因此,类似这样的方法更好,并且可能是解决方法:)

<script>
    import {writable} from "svelte/store";
    import Wrapper from "./Wrapper.svelte";

    const items = [
        {
            id: 1,
            type: "text",
            value: "Test 1"
        },
        {
            id: 2,
            type: "img",
            value: "https://media.giphy.com/media/KzW9EkUE6NJrwqG0UX/giphy.gif"
        }
    ];

    const listStore = writable(items);
</script>

{#each $listStore as item, i (item.id)}
    <Wrapper item={item} store={listStore} myIndex={i} />
{/each}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...