vuejs3

How to disable first space in HTML input?


Consider a simple Vue component with text input (link).

<script setup>
import { ref } from 'vue'

const msg = ref('')

function onInput(e) {
  let value = e.target.value;
  if (value === ' ') value = '';
  msg.value = value;
}
</script>

<template>
  <input :value="msg" @input="onInput" />
</template>

I want to disable first space in this input, i.e. if a user inputs space then the inputfield should remain empty. But it doesn't work. I guess it happens because Vue checks that msg doesn't change and rejects component render.

My next attempt is to force render the component (link).

<script setup>
import {getCurrentInstance} from 'vue'
import { ref } from 'vue'

const msg = ref('')

const instance = getCurrentInstance();

function onInput(e) {
  let value = e.target.value;
  if (value === ' ') {
    value = '';
    msg.value = value;    
    instance?.proxy?.$forceUpdate();
    return;
  }
  msg.value = value;
}
</script>

<template>
  <input :value="msg" @input="onInput" />
</template>

It works, but with limits. My project has a big tree of components and $forceUpdate updates current component only without children.

I also tried to re-render the whole sub-three with changing key on the component, but in this case I lose input's focus.

To sum up, it seems that I miss something. I can't believe that such simple task requires weird workarounds. Please help me to find a simple and "best practice" way to disable first space in html input.

P.S. React version works as expected.

import React, {useState} from 'react';

export function App(props) {
  const [value, setValue] = useState('');

  function onInput(e) {
    let v = e.target.value;
    if (v === ' ') v = '';
    setValue(v);
  }

  return (
    <div className='App'>
      <input value={value} onInput={onInput} />
    </div>
  );
}

Solution

  • Based on your code: don't react to the input event. It's too late. React before the input event occurs. And filter out that space character.

    This will save the application a lot of unnecessary calculations (msg=" ", then msg="").

    <script setup>
      import { ref } from 'vue'
    
      const msg = ref('')
    
      const filterFirstSpace = e => {
        const isEmpty = !e.target.value.length
        const isSpace = /\s/.test(e.data)
    
        if (isEmpty && isSpace) {
          e.preventDefault()
        }
      }
    </script>
    
    <template>
      <input
        :value="msg"
        @beforeinput="filterFirstSpace"
      />
    </template>