svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Increase elements when using a combination of await block and transition

Open hidekatsu-izuno opened this issue 7 years ago • 15 comments

I get a strange behavior when I use a transition in a await block. I write a sample repl script. when you click next button, a inner element increase.

REPL

hidekatsu-izuno avatar Jul 11 '18 12:07 hidekatsu-izuno

Same issue, elements get appended without removing old children

v3: https://svelte.dev/repl?version=3.1.0&gist=a4264dbdc277bd929c0e9673477cc84d

cdock1029 avatar Apr 30 '19 19:04 cdock1029

I'm also facing this Bug! would be nice to hear from someone, it seems like something that can't be toooo complicated to fix...

elkowar avatar Aug 07 '19 11:08 elkowar

Copying my comments over from the other issue:

I'm having the same issue. Here's a slightly smaller reproduction of the issue: REPL

I updated the REPL after a discovery: the bug only appears with transition out and not with transition in.

dasZGFz avatar Aug 26 '19 01:08 dasZGFz

Same issue with transitions not removing the previous DOM nodes

mylastore avatar Sep 20 '19 15:09 mylastore

Same issue awaits duplicates DOM nodes (event without transition) after some graphql mutations (svelte-apollo - mutation updates - cache - svelte store)

jozef-slezak avatar Oct 02 '19 19:10 jozef-slezak

anybody looking into this?

buhrmi avatar Oct 22 '19 03:10 buhrmi

Same issue here. My "solution" is going to be not to use await, and load an enormous amount of data on page load that was previously only fetched on demand. Not ideal.

colinmeinke avatar Nov 27 '19 11:11 colinmeinke

@dasZGFz For this one: REPL If the transition duration < timeout value it works.

Updated duration and timeout as variables: REPL

Somehow in your original example, the count++ inside the transitioned blocked doesn't work. Put that in the load func instead.

jmmaranan avatar Feb 15 '20 17:02 jmmaranan

I would like to fix this. Can I be assigned?

vlasy avatar Mar 19 '20 13:03 vlasy

I would like to fix this. Can I be assigned

Yes sure!

tanhauhau avatar Mar 19 '20 13:03 tanhauhau

Just a quick update, I spent a few hours yesterday reading and debugging through transitions.ts and await_block.ts but with no tangible result.

I am struggling with the whole architecture/interconnections and questions like:

Any info or hints are much appreciated. Thanks :)

vlasy avatar Mar 20 '20 07:03 vlasy

i'm not familiar with transition, but i can help you with await block

What kind of object is info in await_block.ts/handle_promise and what are its block and blocks` fields?

take a look at this await tutorial, https://svelte.dev/tutorial/await-blocks, in the compiled code, there's this part where:

let info = {
  ctx,
  current: null,
  token: null,
  pending: create_pending_block,
  then: create_then_block,
  catch: create_catch_block,
  value: 2,
  error: 3
};

this is the info object.

  • it contains 3 create_fragment function: pending, then, catch
  • blocks will be an array of 3 fragments, [pending, then, catch].
    • it is lazily created, ie:
    • only when render the pending block, you do blocks[0] = pending()
    • so the index is the index to the block, selecting pending, then or catch
    • the type is then the referring to the the create_fragment function, info.pending, info.then, info.catch
  • current stores the current block of the await block, when switching block, you need to unmount the current block.
  • the value and error is the index to ctx to get the resolved value, or rejected error respectively.

tanhauhau avatar Mar 21 '20 03:03 tanhauhau

+1 I am also encountering this issue. Code that causes this is as follows:

<script context="module">
    export function buildAttribute(path, label, filterable, sortable, visible, processFunc = (x) => x) {
        return {
            path,
            label,
            filterable,
            sortable,
            visible,
            processFunc
        }
    }
</script>

<script>
    import SortableHeader from "./SortableHeader.svelte";
    import FilterCell from "./FilterCell.svelte";
    import Spinner from "../Spinner/Spinner.svelte";
    import Button from "../Button/Button.svelte";
    import {fade, fly} from 'svelte/transition';
    import {flip} from 'svelte/animate';
    import better_flip from '../../helpers/animate/better_flip';
    import Icon from "../Icon/Icon.svelte";
    import {onMount} from "svelte";

    export let attributes = [];
    export let data_promise;
    export let data_attribute;
    export let data_key;
    export let small = true;
    let visibleAttributes = attributes.filter(a => a.visible);
    // Data State
    let result = [];
    let display = [];
    $: display = result;

    // Filter and Sort State
    let filters = {};
    let desc = false;
    let sortedAttribute = "";

    // Pagination
    let pageLimit = 15;
    let page = 0;

    export const shim = (p) => {
        data_promise = p.then(i => {
            result = i.data[data_attribute];
            if (result.length > 0) {
                Object.keys(result[0]).forEach(k => filters[k] = "");
                filters = filters;
            }
            return i;
        });
    };
    const sort = (newAttribute) => {
        if (sortedAttribute === newAttribute) {
            if (desc) {
                desc = false;
                sortedAttribute = "";
                display = result;
                filters = filters;
                return;
            } else {
                desc = !desc;
            }
        } else {
            sortedAttribute = newAttribute;
            desc = false;
        }
        update();
    };
    const update = () => {
        if (display.length > 0 && Object.keys(display[0]).includes(sortedAttribute))
            display = [...display.sort((a, b) =>
                    (a[sortedAttribute].toString().toUpperCase() < b[sortedAttribute].toString().toUpperCase())
                            ? desc ? 1 : -1
                            : (a[sortedAttribute].toString().toUpperCase() > b[sortedAttribute].toString().toUpperCase())
                            ? desc ? -1 : 1
                            : 0)];
    };
    const lookup = (object, path) => {
        let segments = path.split(".");
        let output = "";
        let temp = object;
        for (let s in segments) {
            s = segments[s];
            if (temp.hasOwnProperty(s)) {
                temp = temp[s];
            } else {
                return "";
            }
        }
        return temp || "";
    };

    // Shim to let me mutate query results while still using the await block
    shim(data_promise);

    // Handle Filtering
    $: if (filters) {
        let internal = result;
        for (let path in filters) {
            internal = internal.filter(o => lookup(o, path).toString().toLowerCase().includes(filters[path].toLowerCase()));
        }
        display = internal;
        update();
    }
</script>

<table class="uk-table uk-position-relative uk-table-striped uk-table-middle  uk-table-responsive"
       class:uk-table-small={small}>
    <thead>
    <tr>
        {#each visibleAttributes as attribute}
            {#if attribute.sortable}
                <SortableHeader path={attribute.path} {sort} {sortedAttribute} {desc} label={attribute.label}
                                style={"width:" + 100 / (visibleAttributes.length+1) + "%;"}/>
            {:else}
                <td style={"width:" + 100 / (visibleAttributes.length+1) + "%;"}>{attribute.label}</td>
            {/if}
        {/each}
        <td style={"width:" + 100 / (visibleAttributes.length+1) + "%;"}>
            <div class="uk-flex uk-flex-middle">
                <label style="flex:4">Items per page</label>
                <select class="uk-select uk-flex-1" bind:value={pageLimit}>
                    <option value="5">5</option>
                    <option value="15" selected>15</option>
                    <option value="30">30</option>
                </select>
            </div>
        </td>
    </tr>
    {#if attributes.filter(a=>a.visible && a.filterable).length > 0}
        <tr>
            {#each visibleAttributes as attribute}
                {#if attribute.filterable}
                    <FilterCell bind:value={filters[attribute.path]}/>
                {/if}
            {/each}
            <td>
                {#if display.length > pageLimit}
                    <div class="uk-flex uk-flex-center uk-flex-middle">
                        {#if page > 0}
                            <Icon icon="chevron-left" on:click={()=>page--}/>
                        {:else}<span style="width:1em;"></span>
                        {/if}
                        <span>{page+1}</span>
                        {#if display.length > (page+1) * pageLimit}
                            <Icon icon="chevron-right" on:click={()=>page++}/>
                        {:else}<span style="width:1em;"></span>
                        {/if}
                    </div>
                {/if}
            </td>
        </tr>
    {/if}
    </thead>
    <tbody>
    {#await data_promise}
        <Spinner show={true}/>
        {#each Array(4) as i}
            <tr>
                {#each visibleAttributes as attribute}
                    <td>
                        <div class="placeholder"></div>
                    </td>
                {/each}
                <td class="uk-flex uk-flex-around">
                    <Button>
                        <Icon icon="refresh"/>
                    </Button>
                    <Button>
                        <Icon icon="pencil"/>
                    </Button>
                    <Button>
                        <Icon icon="trash"/>
                    </Button>
                </td>
            </tr>
        {/each}
    {:then r}
        {#each display.slice(page*pageLimit, page*pageLimit + pageLimit) as item, i (item[data_key])}
            <tr out:fly={{duration:500,delay:5 * i, x:50}} in:fly={{delay: 100*i, duration: 500, x:-50}}
                animate:flip="{{duration:500}}" class="uk-background-default">
                {#each visibleAttributes as attr}
                    <td>{attr.processFunc(lookup(item, attr.path))}</td>
                {/each}
                <td>
                    <slot name="buttons" id={item[data_key]} object={item}/>
                </td>
            </tr>
        {/each}
    {/await}
    </tbody>
</table>

ItsMeBrianD avatar Jun 30 '20 19:06 ItsMeBrianD

Still actual!

gyurielf avatar Aug 27 '21 11:08 gyurielf

i've checked that the first REPL from the original author has already fixed with the latest version of Svelte.

@0c370t your example is a bit big, you wanna check if this is already fixed on the latest Svelte?

tanhauhau avatar Sep 12 '22 17:09 tanhauhau

Closing as it's no longer reproducible

dummdidumm avatar Mar 14 '23 15:03 dummdidumm