开发者问题收集

可重复使用的输入组件,带有 vue 3 + typescript + Composition API

2022-11-28
1824

我有以下(atom)输入组件 Element UI(Element Plus for vue 3)作为基础组件。

atom/input/index.vue

<template>
  <el-form-item :label="label">
    <ElInput
      :value="modelValue"
      @input="$emit('update:modelValue', handleInputChange($event))"
    >
      <template v-if="prepend" #prepend>Http://</template>
      <template v-if="append" #append>.com</template>
    </ElInput>
  </el-form-item>
</template>
<script setup lang="ts">
  import { ElInput, ElFormItem } from "element-plus"
  interface IInput {
    label: string
    modelValue: any
  }
  const { label, modelValue } = defineProps<IInput>()

  const handleInputChange = (event: Event) => {
    console.log(event)
    return (event.target as HTMLInputElement).value
  }
</script>

在我的主页组件中:

components/home.vue

<template>
  <Input :label="'Book title'" v-model="title" />
<br/>
<h1>{{title}}</h1>
</template>
<script setup lang="ts">
  import { ref } from "vue"
  import Input from "./atom/input/index.vue"
  const title = ref<string>("")
</script>

通过上述代码设置,组件以其标签以正确的形式显示。

但是当我开始在输入组件中输入时,控制台中出现以下错误。

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'value')

我还在控制台中记录了该事件,它返回了我输入的字符。

此外,我收到错误消息:

Argument of type 'string' is not assignable to parameter of type 'Event'.

在我的代码行中: @input="$emit('update:modelValue', handleInputChange($event))"

我可以删除我的类型转换: handleInputChange(<Event | unknown>$event)

我还尝试使用具有相同值的 html 输入标签创建可重复使用的输入组件并发出如上所述的内容,并且它没有任何错误地工作。

有人可以帮我找出我在这里遗漏了什么吗?

更新:

根据 Duannx 的建议,我更改了函数的返回值:

const handleInputChange = (event: any) => {
    return event
  }

但是现在,当我在输入字段中输入时,第一个字符被第二个字符替换,第二个字符被第三个字符替换,依此类推。

这是 element ui 游乐场中问题的重现:

演示网址

3个回答

我发现我必须在计算属性中使用 set 和 get 方法。

<template>
  <el-form-item :label="label">
    <ElInput
      :autofocus="true"
      :type="type"
      :placeholder="placeholder"
      :show-word-limit="showWordLimit"
      :clearable="clearable"
      :show-password="showPassword"
      :suffix-icon="suffixIcon"
      :prefix-icon="prefixIcon"
      :size="size"
      :max-length="maxLength"
      v-model="val"
    >
      <template v-if="prepend" #prepend>Http://</template>
      <template v-if="append" #append>.com</template>
    </ElInput>
  </el-form-item>
</template>
<script setup lang="ts">
  import { computed, toRefs } from 'vue'
  import { ElInput, ElFormItem } from "element-plus"
  interface IInput {
    label: string
    type?: string
    placeholder?: string
    maxLength?: number
    showWordLimit?: boolean
    clearable?: boolean
    showPassword?: boolean
    suffixIcon?: any
    prefixIcon?: any
    size?: string
    prepend?: any
    append?: any
    modelValue: any
  }
 
  const props = defineProps<IInput>()
  const {
    label,
    type,
    placeholder,
    maxLength,
    showWordLimit,
    clearable,
    showPassword,
    suffixIcon,
    prefixIcon,
    size,
    prepend,
    append,
    modelValue,
  } = toRefs(props)
  
  const emit = defineEmits(['update:modelValue'])

  const val = computed({
    get() {
      return modelValue.value
    },
    set(val) {
      emit('update:modelValue', val)
    }
  })
</script>
psudo
2022-11-30

元素 plus input 的 input 事件传递的是一个值 ( string | number ),而不是 Event 类型。因此您可以直接使用 $event 作为值。

<ElInput
      :value="modelValue"
      @input="$emit('update:modelValue', $event)"
>
      <template v-if="prepend" #prepend>Http://</template>
      <template v-if="append" #append>.com</template>
</ElInput>
Duannx
2022-11-29

在您的 atom/input/index.vue 中,您只需传递 v-bind="$attrs"。$attrs 使 v-model、占位符和任何其他侦听器工作。v-bind="$attrs" 包括所有非 prop 属性

<ElInput v-bind="$attrs" />
chayi
2022-12-01