为什么 Input 不将输入的数据存储在 modelValue Prop 中

Why does Input not storing inputted data in modelValue Prop

我已经阅读了 Vue 3 上关于 v-model:value 的几篇文章。 但是 none 在这种情况下确实帮助了我。

我想在 modelValue prop 中存储值,并希望在用户更新或输入时更新它。

此代码运行没有任何错误,但在 Vue 3 开发控制台中显示 modelValue: undefined

我的 Vue 代码:

    <template>
  <span :class="wrapperClass">
    <i :class="iconLeft" v-if="iconLeft" />
    <InputText
      :type="type"
      :value="modelValue"
      :class="{ 'p-filled': filled }"
      @input="onInput"
    />
    <label for="username">{{ label }}</label>
    <i :class="iconRight" v-if="iconRight" />
  </span>
</template>
<script lang="ts">
import InputText from "primevue/inputtext"
import { defineComponent } from "vue"
export default defineComponent({
  name: "FormInput",
  emits: ["update:modelValue"],
  components: { InputText },
  props: {
    iconLeft: {
      type: String,
      default: "",
    },
    iconRight: {
      type: String,
      default: "",
    },
    error: {
      type: String,
      default: "",
    },
    label: {
      type: String,
      default: "",
    },
    type: {
      type: String,
    },
    modelValue: {
      type: String,
    },
  },
  computed: {
    wrapperClass(): any {
      return {
        "p-float-label": true,
        "p-input-icon-left": this.iconLeft,
        "p-input-icon-right": this.iconRight,
      }
    },
    filled(): any {
      return this.modelValue != null && this.modelValue.toString().length > 0
    },
  },
  methods: {
    onInput(event: any): any {
      this.$emit("update:modelValue", event.target.value)
    },
  },
})
</script>

父组件:

<template>
  <div class="home">
    <Example :example="{ heading: 'fii' }" />
    <Button label="Test" />
    <hr />
    <img alt="Vue logo" src="../assets/logo.png" />
    <hr />
    <FormInput v-bind="args" />

    <hr />

    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue"
import HelloWorld from "../components/HelloWorld.vue" // @ is an alias to /src
import Example from "../components/Example/Example.vue" // @ is an alias to /src
import FormInput from "../components/Form/FormInput.vue"

export default defineComponent({
  name: "Home",
  components: {
    HelloWorld,
    Example,
    FormInput,
  },
  setup() {
    return {
      args: {
        label: "test",
        type: "text",
        value: "",
      },
    }
  },
})
</script>

请看这张截图

开发控制台中的父组件

需要解决的问题很少:

  1. PrimeVue组件InputText没有道具value但是modelValue(支持v-model)-InputText Docs + read Using v-model on Components学习区别本机输入 v-model 与自定义组件之间

  2. InputText 不是原生 input 元素,因此没有必要监听 input 事件。该组件正在发出 update:modelValue 事件。此外,事件负载不是本机事件,因此 event.target.value 没有意义,只需使用 value - source

  3. 没有必要复制 p-filled,因为 InputText 已经这样做了(参见上面的来源)

// FormInput.vue

<template>
  <span :class="wrapperClass">
    <i :class="iconLeft" v-if="iconLeft" />
    <InputText
      :type="type"
      :modelValue="modelValue"
      @update:model-value="onInput"
    />
    <label for="username">{{ label }}</label>
    <i :class="iconRight" v-if="iconRight" />
  </span>
</template>
<script lang="ts">
import InputText from "primevue/inputtext"
import { defineComponent } from "vue"

export default defineComponent({
  name: "FormInput",
  emits: ["update:modelValue"],
  components: { InputText },
  props: {
    iconLeft: {
      type: String,
      default: "",
    },
    iconRight: {
      type: String,
      default: "",
    },
    error: {
      type: String,
      default: "",
    },
    label: {
      type: String,
      default: "",
    },
    type: {
      type: String,
      default: "text"
    },
    modelValue: {
      type: String,
      required: true
    },
  },
  computed: {
    wrapperClass(): any {
      return {
        "p-float-label": true,
        "p-input-icon-left": this.iconLeft,
        "p-input-icon-right": this.iconRight,
      }
    },
  },
  methods: {
    onInput(value: any): any {
      this.$emit("update:modelValue", value)
    },
  },
})
</script>

用法:

<FormInput v-model="data" />
<!-- OR -->
<FormInput :modelValue="data" @update:model-value="data = $event" />

演示:

const app = Vue.createApp({
  data() {
    return {
      text: "default value",
    }
  },
});

app.component('FormInput', {
  emits: ["update:modelValue"],
  components: {
    'inputtext': primevue.inputtext
  },
  template: `
  <span :class="wrapperClass">
    <i :class="iconLeft" v-if="iconLeft" />
    <inputtext
      :type="type"
      :modelValue="modelValue"
      @update:model-value="onInput"
    ></inputtext>
    <label for="username">{{ label }}</label>
    <i :class="iconRight" v-if="iconRight" />
  </span>
  `,
  props: {
    iconLeft: {
      type: String,
      default: "",
    },
    iconRight: {
      type: String,
      default: "",
    },
    error: {
      type: String,
      default: "",
    },
    label: {
      type: String,
      default: "",
    },
    type: {
      type: String,
      default: "text"
    },
    modelValue: {
      type: String,
      required: true
    },
  },
  computed: {
    wrapperClass() {
      return {
        "p-float-label": true,
        "p-input-icon-left": this.iconLeft,
        "p-input-icon-right": this.iconRight,
      }
    },
  },
  methods: {
    onInput(value) {
      this.$emit("update:modelValue", value)
    },
  },
});

app.mount('#app');
<link href="https://unpkg.com/primevue/resources/themes/saga-blue/theme.css" rel="stylesheet">
<link href="https://unpkg.com/primevue/resources/primevue.min.css" rel="stylesheet">
<link href="https://unpkg.com/primeicons/primeicons.css" rel="stylesheet">

<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/primevue/inputtext/inputtext.min.js"></script>

<div id="app">
  <form-input v-model="text"></form-input>
  <pre>
    {{ text }}
  </pre>
</div>