Marcel A
Marcel A

Reputation: 21

Svelte export value but render it differently internally

I'm having a problem with my svelte Component. I have something similar to this:

export let date = new Date();
let internalDate = dateToInternalDate(date);
$: internalDate = dateToInternalDate(date);

//this wont work because of circular dependency:
$: date = internalDateToDate(internalDate);

I want to have my date value displayed as a formatted string internally. It should be bound to an input field like so:

<!-- eg. formatted like: DD.MM.YYYY -->
<input type="text" bind:value={internalDate}>

My parent Component shall import this child like this:

<MyDateComponent bind:date={myJsDateValue}>

I would like to be able to modify the date value from my child & my parent component.

What would be the most elegant way of doing this?

Thanks in advance

Upvotes: 1

Views: 429

Answers (1)

rixo
rixo

Reputation: 25001

You can break the circular dependency by ensuring the destination variable doesn't appear in the reactive block. You can do this by keeping only the source variable to trigger the change in the reactive block, and extracting the destination variable into a function (hence, outside of the reactive block).

MyDateComponent.svelte

<script>
    export let date = new Date();
    
    let internalDate = ''
    
    const dateToInternalDate = x => {
        const d = new Date(x)
        return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
    }
    
    const internalDateToDate = x => {
        if (!/^\d{4}-\d{2}-\d{2}$/.test(x)) return
        const d = new Date(x)
        // bail out on invalid date
        if (isNaN(d.getTime())) return
        return d
    }
        
    const input = x => {
        internalDate = dateToInternalDate(x)
    }
    
    const output = x => {
        const v = internalDateToDate(x)
        if (v) date = v
    } 
    
    $: input(date)

    $: output(internalDate)
</script>

<!-- eg. formatted like: DD.MM.YYYY -->
<input type="text" bind:value={internalDate}>

App.svelte

<script>
    import MyDateComponent from './MyDateComponent.svelte'
    
    let date
</script>

<MyDateComponent bind:date />

<button on:click={() => {date = '2020-06-30'}}>2020-06-30</button>

<button on:click={() => {date = '2019-06-30'}}>2019-06-30</button>

<pre>Date: {date}</pre>

REPL

Upvotes: 3

Related Questions