svelte

Why do I need to explicitly set <input>.value to make the user-showing text in an input change?


In the following component (range.svelte) why do I have to do inputElement.value = valueToSet; at line 24 when the input element value is bound to value (<input value={value}...)? Why isn't the assignment to value just before at line 27 sufficient?

Some more context: the idea is for the user not to be able to enter a number outside of the range in the "number" type input.

<script lang="ts">
    import { RangeSlider } from '@skeletonlabs/skeleton';

    export let value: number;
    export let suffix: string;
    export let min: number;
    export let max: number;

    export let name: string;
    export let step: number = 1;

    let clazz: string = ""

    export {clazz as class};

    function onInputChanged(event: Event): void {
        const inputElement = event.target;

        const newValue = inputElement.value;

        const valueToSet = computeValueToSet(newValue);

        value = valueToSet
        inputElement.value = valueToSet;
    }

    function computeValueToSet(newValue: string): number {
        if (newValue === "") {
            return value;
        }

        const newInt = parseInt(newValue, 10);

        const isInRange = min <= newInt && newInt <= max;
        if (!isInRange) {
            return value;
        }

        return newInt;
    }
</script>

<div class="flex flex-row gap-4 {clazz}" {...$$restProps}>
    <RangeSlider name={name} class="basis-3/4" bind:value {min} {max} {step}>
        <span class="flex justify-between">
            <span class="text-xs">{min}{suffix}</span>
            <span class="text-xs">{max}{suffix}</span>
        </span>
    </RangeSlider>
    <span class="basis-1/4 flex flex-row gap-1 items-center">
        <input
            class="input text-sm w-auto"
            type="number"
            value={value}
            on:input={onInputChanged}
            {min}
            {max}
            {step}
        />
        <span>{suffix}</span>
    </span>
</div>


Solution

  • Svelte updates the DOM on change. If the new value is outside the valid range, you effectively set value to value, hence the state is determined to have not changed and no update is performed.

    You could force an update by first invalidating the variable via a completely different value, e.g.

    value = null;
    value = valueToSet;
    

    (This might only work with the reactivity model of Svelte 3/4.)