Can i refactoring from repeating code this vue custom modifiers in multi v-model?

Imboyo

I made multiple v-model binding in vue 3. but got some probel in code quality when i must code emit value function repeating with 90% the function code is same. Is this possible to refactoring this code

<script lang="ts" setup>
const props = defineProps({
  firstName: String,
  lastName: String,
  firstNameModifiers: {
    default: {
      default: () => ({}),
      capitalize: () => ({}),
    },
  },
  lastNameModifiers: {
    default: {
      default: () => ({}),
      capitalize: () => ({}),
    },
  },
});
const emit = defineEmits(["update:firstName", "update:lastName"]);

// ! TODO: Make Function to handle modifiers - its repeating below
function firstNameEmitValue(e: Event) {
  const target = e.target as HTMLInputElement;
  let value = target.value;
  if (props.firstNameModifiers["capitalize"]) {
    value = value.charAt(0).toUpperCase() + value.slice(1);
  }
  emit(`update:firstName`, value);
}

function lastNameEmitValue(e: Event) {
  const target = e.target as HTMLInputElement;
  let value = target.value;
  if (props.firstNameModifiers["capitalize"]) {
    value = value.charAt(0).toUpperCase() + value.slice(1);
  }
  emit(`update:lastName`, value);
}
</script>

<template>
  <input type="text" :value="firstName" @input="firstNameEmitValue" />
  <input type="text" :value="lastName" @input="lastNameEmitValue" />
</template>

Its ok when just using 1 modifiers. But its will become annoying if i want to add other modifiers. for example toUppercase, toLowerCase etch. Maybe the solution is just separate the component for firstname input and lastname input and make it as single v-model and emit.

But i just want to try this approach bcause vue put it in, in their documentation.

Events Documentation

tao

This should do it:

helpers.ts

export const names = ["first", "last"]

your component

<script lang="ts" setup>
  import { reactive, computed } from "vue"
  import { names } from '../path/to/helpers'

  const modifiers = {
    default: {
      default: () => ({}),
      capitalize: () => ({})
    }
  }

  const props = defineProps(
    Object.assign(
      {},
      ...names.map((name) => ({
        [name + "Name"]: String,
        [name + "NameModifiers"]: modifiers
      }))
    )
  )
  const emit = defineEmits(names.map((name) => `update:${name}Name`))
  const emitValue = ({ target }: Event, name: string) => {
    if (target instanceof HTMLInputElement) {
      let { value } = target
      if (props[`${name}NameModifiers`]["capitalize"]) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      emit(`update:${name}Name`, value)
    }
  }
  const state = reactive(
    Object.assign(
      {},
      ...names.map((name) => ({ [name]: computed(() => props[name]) }))
    )
  )
</script>

<template>
  <input
    type="text"
    v-for="name in names"
    :key="name"
    :value="state[name]"
    @input="emitValue($event, name)"
  />
</template>

If you add 'middle' to names, it should work out of the box.

Note: haven't tested it, since you haven't provided a sandbox. If it doesn't work, I'll make one and test it.


I tend to avoid @input + :value (although it works), in favor of v-model with computed setter. Has the advantage of not having to cast the input type and also allows assigning to model programmatically, should you ever need it. I also find the resulting syntax cleaner, hence more readable:

<script lang="ts" setup>
  import { reactive, computed } from "vue"
  import { names } from '../path/to/helpers'

  const modifiers = {
    default: {
      default: () => ({}),
      capitalize: () => ({})
    }
  }

  const props = defineProps(
    Object.assign(
      {},
      ...names.map((name) => ({
        [name + "Name"]: String,
        [name + "NameModifiers"]: modifiers
      }))
    )
  )
  const emit = defineEmits(names.map((name) => `update:${name}Name`))
  const state = reactive(
    Object.assign(
      {},
      ...names.map((name) => ({
        [name]: computed({
          get: () => props[name],
          set: (val) =>
            emit(
              `update:${name}Name`,
              props[`${name}NameModifiers`]["capitalize"]
                ? val.charAt(0).toUpperCase() + val.slice(1)
                : val
            )
        })
      }))
    )
  )
</script>

<template>
  <input type="text" v-for="name in names" :key="name" v-model="state[name]" />
</template>

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Refactoring a library to be async, how can I avoid repeating myself?

How can I prevent a method called by a Vue component from running every time the v-model changes?

Calling vue v-model from inside jquery code

can i search from multi database in my model using django?

How can I bind multiple values from array in v-model for form-input on vue.js?

Code refactoring: from $ to [[

How can I do refactoring of CDK code using TypeScript?

Refactoring vue2 code

How to limit multiple input fields (which use v-model) to only accept numbers in Vue.js (without repeating code for every input field)?

How can I implement custom code to be called from an email?

Can I use @ConditionalProperty with my custom annotation to avoid repeating configuration?

how can I remove generic type from class using refactoring

How can I use v-model with a functional template component in Vue?

How can I avoid repeating code initializing a hashmap of hashmap?

How can I avoid repeating code initializing a hashmap of hashmap?

How can I stop repeating the same code when querying a database

How can I avoid repeating this struct code for my tests

How can I simplify my jQuery code to avoid repeating instructions?

How can I make this repeating code look neater

How can I refactor these two functions with repeating code?

How can I condense my code to avoid repeating for loops?

vuejs2 components code refactoring to avoid repeating element

How can i add tow property in my model in EF code first from one model?

How can I create a computed array in Vue and then render it using v-if all while using v-model on the computed array?

With Pydantic V2 and model_validate, how can I create a "computed field" from an attribute of an ORM model that IS NOT part of the Pydantic model

How declare v-model for custom component in vue render function?

Vue.js custom select component with v-model

Vue 3 custom checkbox component with v-model and array of items

How can I associate names from first column with repeating rows?