3

could anyone can give me a hint about reusable component input in vue 3? Like this in React:

Let say,

  1. use <Input /> reusable component in parent component
import { useState } from 'react';
import Input from './component/Input';

function Home() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    
    console.log(email, password);
  }

  return (
    <form onSubmit={handleSubmit}>
      <Input id="email" labelText="Email" value={email} onChange={e => setEmail(e.target.value)} />
      <Input id="password" labelText="Password" value={password} onChange={e => setPassword(e.target.value)} />
      <button type="submit">Login</button>
    </form>
  );
}
  1. here <Input /> component:
export default function Input(props) {
  const { id, labelText, value, onChange } = props;

  return (
    <label htmlFor={id}>{labelText}<label>
    <input id={id} value={value} onChange={onChange}>
  );
}

4 Answers 4

11

You should read up on Component Basics from the Vue 3 docs.

Key concepts:

  • Use the v-bind directive (or : prefix for shorthand) for data binding
  • Use the v-on directive (or @ prefix for shorthand) for event handlers
  • Use double curly brackets for string interpolation of data properties
  • Use the v-model directive for two-way data binding
  • Use props option to declare properties
  • Use ref to create data properties using Composition API

The equivalent input component in Vue 3:

<template>
  <label :for="id">{{ labelText }}</label>
  <input
    :value="modelValue"
    @change="$emit('update:modelValue', $event.target.value)"
    :id="id"
  />
</template>

<script>
export default {
  props: {
    id: String,
    labelText: String,
    modelValue: String,
  },
};
</script>

And the form:

<template>
  <form @submit="handleSubmit">
    <MyInput id="email" labelText="Email" v-model="email" />
    <MyInput id="password" labelText="Password" v-model="password" />
    <button type="submit">Login</button>
  </form>
</template>

<script>
import MyInput from "@/components/MyInput.vue";
import { ref } from "vue";

export default {
  components: {
    MyInput,
  },
  setup() {
    const email = ref("");
    const password = ref("");

    return {
      email,
      password,
      handleSubmit(e) {
        e.preventDefault()
        console.log("submit", email.value, password.value);
      },
    };
  },
};
</script>

demo

Sign up to request clarification or add additional context in comments.

Comments

2

2023 update Using the Vue composition API (recommended way):

   // MyInput.js
    <template>
    <label :for="id"> {{ label }} </label>
    <input 
       :id="id" 
       :value="modelValue" 
       type="text" 
       @input="$event => emit('update:modelValue',   
       $event.target.value)" :placeholder="placeholder"
     />
    </template>

   <script  setup>
    defineProps(['modelValue', 'label', 'id', 'placeholder'])
    const emit = defineEmits(['update:modelValue'])
   </script>

and you use it like this in Vue 3 component:

   <template>
    <Input id="my-id" label="my label" 
        v-model="myInputValue" />
   <template>
   <script setup>
    const myInputValue = ref('')
   </script>

Comments

0

New approach how to use v-model in a reusable input component with defineModel Vue 3.4; more details on the Medium

// How to define the component (native HTML) 
// components/VInput.vue
<script setup>
const model = defineModel()
</script>

<template>
  <input v-model="model" />
</template>

/* ***** */
// How to use the component (native HTML)
// App.vue
<VInput v-model="userName" />

If you use previous versions of vue.js (more old), you can try to use macro define-models or use solutions described in other answers

Comments

0

I have created a form input component that offers more features with less code.

<template>
  <div class="mb-6">
    <!-- form label -->

    <!-- 
      I have included default classes in this code which can be overwritten if you add a new class. For instance, the default class is "text-red-500". If you add a class to the parent element, only this class will be overwritten, while the other classes will remain in their default state.
    -->

    <!-- If the parent element does not have a label, the label for the input tag will be automatically hidden. -->
    <label
      v-if="label.value !== ''"
      :for="id"
      :class="[labelClass, 'block mb-2 text-md font-medium text-red-500']"
    >
      {{ label }}
    </label>

    <!-- form input -->

    <!-- 
      I have included default classes in this code which can be overwritten if you add a new class. For instance, the default class is "bg-gray-300". If you add a class to the parent element, only this class will be overwritten, while the other classes will remain in their default state.
    -->
    <input
      :id="id"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
      v-bind="$attrs"
      :class="[
        'block',
        'bg-gray-50',
        'border',
        'border-gray-300',
        'text-gray-900',
        'text-md',
        'font-medium',
        'rounded-lg',
        'focus:outline-red-500',
        'block',
        'w-full',
        'p-2.5',
      ]"
    />
  </div>
</template>

<script setup>
defineProps({
  label: {
    type: String,
    default: "",
  },
  modelValue: {
    type: String,
    default: "",
  },
  id: {
    type: String,
    default: "input",
  },

  //classes for form label
  labelClass: {
    type: Array,
    default: [],
  },
});
</script> 

You can use it like this.

<FormInputMedium
  id="email"
  v-model="email"
  type="text"
  placeholder="Enter your email"
  required=""
  class="text-md"
  label="Your Email"
  labelClass="text-blue-500"
 />

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.