Home > Net >  In Svelte, is there a way to undo invalid user input, without changing the state?
In Svelte, is there a way to undo invalid user input, without changing the state?

Time:11-19

In Svelte, is there a way to undo invalid user input, without triggering a momentarily fake property change for state?

For example (try it online), when user types in 123a in the input box, I want to covert it to a number and assign it counter.

For that, I have to introduce a temporary and redundant change to counter (using Number.NaN below). Otherwise, counter won't change, and 123a will remain inside the input box, while I want to revert it back to 123:

<script>
    let count = 0;
    const handleClick = () => count  ;
    const handleChange = async e => {
        const userValue = e.target.value;
        let newValue = userValue? parseInt(userValue): 0;
        if (isNaN(newValue)) newValue = count;
        count = Number.NaN;
        await Promise.resolve();
        count = newValue;           
    };  
</script>

Count: <button on:click={handleClick}>{count}</button>
<br />
A: <input value={count} on:input={handleChange} />
<br />

Is there a better way of doing it? Essentially, I want trigger a manual re-render for a part of my Svetle component, without changing the state.

Of course, in the event handler I could undo the change just by modifying e.target.value directly:

const handleChange = async e => {
    const userValue = e.target.value;
    let newValue = userValue? parseInt(userValue): 0;
    if (isNaN(newValue)) newValue = count;
    if (newValue == count)
        e.target.value = count;
    else
        count = newValue;
};  

But I wonder if there's a way to tell Svelte to re-render the whole <input>?

To compare, here's how I could do it in React (try it online):

function App() {
  let [count, setCount] = useState(0);
  const handleClick = () => setCount((count) => count   1);
  const handleChange = (e) => {
    const userValue = e.target.value;
    let newValue = userValue ? parseInt(userValue) : 0;
    if (isNaN(newValue)) newValue = count;
    setCount(newValue);
  };
  return (
    <>
      Count: <button onClick={handleClick}>{count}</button>
      <br />
      A: <input value={count} onInput={handleChange} />
      <br />
    </>
  );
}

CodePudding user response:

You simply need to bind count to the value of the input. REPL

<script>
  let count = 0;
  
  const handleClick = () => count  ;
  
  const handleChange = (e) => {
    const userValue = e.target.value;
    const newValue = userValue ? parseInt(userValue, 10) : 0;
  
    count = isNaN(newValue) ? count : newValue;
  };    
</script>

Count: <button on:click={handleClick}>{count}</button>
<br />
A: <input bind:value={count} on:input={handleChange} />
<br />

Note: Remember to always pass a radix to parseInt(), it does not default to 10.

  • Related