Svelte - web development

Svelte REPLs

Don't forget your saved Svelte REPLs. There maybe helpful things in there. 
        

Export function to Svelte file

# Svelte file
<script>
import capitalize from '../helpers/string'
console.log(capitalize('hey'))
</script>

# ../helpers/string.js
export function capitalize(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

        

Snippet for small saved notification

# See source file, jekyll jankifies this
{#if newTabCreation}
    
{#if tabJustSaved}
Saved!
{/if}
{/if}

Iterate over Object.entries

{#each Object.entries(categoryDefinitions) as [name, categoricalDefinitions], i}
    <DefineCategory name={name} {categoricalDefinitions} />
{/each}
        

Bind to iterable being iterated over while in loop

{#each categoryDefinitions as category, i}
<DefineCategory bind:category={categoryDefinitions[i]} storageKey={''}
    <div class="text-xs font-extrabold text-red-400 hover:text-red-500 cursor-pointer top-2 relative" slot="delete" on:click={() => deleteCategory(i)}>
        X
    </div>
</DefineCategory>
{/each}

What is happening here is basically you shouldn't bind category to category (ie: bind:category={category})

That doesn't work. However if you bind to the index of the looped object, it does work.
This also applies for objects but using the key instead of an index.

Always make your changes directly to the variable that is defined outside of the loop.
        

Append something to the body of an element

Wrap the element in the <Portal> code as shown below
        

Portal code

# This portal code is useful for making the body the parent of an element.
Simply wrap the desired element or component with <Portal> tag and the element should be positioned relative to the body.

<Portal>
    <div>This is appended to the body</div>
</Portal>


<script>
    // src/components/Portal.svelte
    import { onMount, onDestroy } from "svelte";
    let ref;
    let portal;

    onMount(async () => {
        portal = document.createElement("div");
        portal.className = "portal";
        document.body.appendChild(portal);
        portal.appendChild(ref);
    });

    onDestroy(() => {
        document.body.removeChild(portal);
    });
</script>

<style>
    .portal-clone {
        display: none;
    }
</style>

<div class="portal-clone">
    <div bind:this={ref}>
        <slot />
    </div>
</div>
        

Positioning something absolutely while also having a transition

Transitions require that the element be not even created yet and so you cannot bind to them.

This is a hacky workaround that says basically make the element but make it invisible and absolute
(if absolute is desired in the end product) in order to get the exact width of the element.

Here is the relevant (not full) code:

<script>

const POSITIONS = {
    'top-left': `left: 50px; top: 50px;`,
    'top-center': (translateX, translateY) =>  `left: calc(50% - ${translateX}px); top: 50px;`,
}

function setPosition(pos, open) {
    let [xOffset, yOffset] = handleForOffset();
    let modalWidth;
    let modalHeight;
    let translateX;
    let translateY;
    try {
        modalWidth = modal.offsetWidth;
        modalHeight = modal.offsetHeight;
        translateX = modalWidth / 2;
        translateY = modalHeight / 2;
    } catch {
        translateX = 0;
        translateY = 0;
    }
    style = POSITIONS[pos](translateX, translateY)
}

</script>

{#if example}

    <button on:click={() => open = true}>Open Modal</button>
    <svelte:self bind:open={open}>
        <div class="px-4 py-2 bg-slate-800">
            This is an example modal
        </div>
    </svelte:self>

{:else}

    {#if open}
        <Portal>
            <div bind:this={modal}
                in:fly=
                out:fly=
                class="modal absolute z-50 shadow-8"
                style="{style}">
                <slot />
            </div>
        </Portal>

    {:else}
        <div bind:this={modal} class="absolute invisible"><slot /></div>
    {/if}

    <DocumentBackdrop bind:visible={open} />

{/if}
        

Generating Example for component

This is a small example of how to pass necessary variables to the "non-example" component from the "example" component.

<script>
    import BaseModal from "../../modals/BaseModal.svelte";

    export let example = false;

    export let open = false;

    export let position = 'middle-center'
</script>

{#if example}

    <button on:click={() => open = true}>Open Modal</button>
    <svelte:self bind:open={open} position={position}>
        <div slot="title">Delete Category</div>
        <div slot="description">Are you sure you want to delete this category?</div>
    </svelte:self>

{:else}

    <BaseModal bind:open={open} position={position} >
        hey
    </BaseModal>
{/if}

        

Call parent function from child

In this example the following assumptions are made:

    The "parent component" is the component that is inherited from, NOT a component that "houses" the child
    The "child component" basically inherits from the parent component and makes assumptions about certain variables

For example the BaseModal would be the parent, and the Delete modal (which inherits from BaseModal) would be the child.


Ok so I think I found a solution to this.

Bind the child component to a variable, then we can call any function that is exported by the parent

Example:

    Chart bind:this={chartRef}

Then in the script tag we can say this:

    chartRef.exportedFunction();

In the parent component we have to export said function in regular script tag:

    export function exportedFunction() => { doStuff(); }

This is not tested but I think it makes sense


Giving up on this and just defaulting to defining the same needed functions in the children.

This doesn't seem to be a huge issue, as in the case with the Delete modal in the component library.

Simply bind the relevant export variables and copy/paste in the needed functions.

This, for now, is much easier to understand/implement than any alternative.

Svelte was not designed to have object oriented inheritance, so this is something we will simply have to deal with.
Its not a huge deal.



        

Animations stopping new page from rendering immediately

Use the local tag to make transitions only occur when the element itself is added or deleted.
By default it includes any parent that is added or deleted.

in:fly|local=
out:fly|local=

transition:fly|local

        

Troubleshooting


        

Dynamic Named Slots

Dynamic named slots are not supported. Right now I have no good solution, unfortunately.
        

Get filename of current file

# this has worked in the past
console.log($$props.$$scope.ctx[3].name);