I have a useful little custom input I've been using in all of my Vue2 projects (allows you to customize with autofocus, autogrow and debounce), but now that I am working in Vue3, I've been trying to create an updated version.
I've not completed it yet, but I've come across some trouble with the Vue3's composition API :value="modelValue"
syntax. In my CodeSandbox I have two inputs, one using the new syntax and the other just straight up using v-model
. The later works, while the :value="valueInner"
throws Extraneous non-props attributes
errors.
What am I doing wrong here and how can I get this to work with that :value="modelValue"
syntax?
Cheers!
NOTE: I still need to add autogrow and add all the usecases in App.vue.
CInput
<template>
<input
ref="inputRef"
data-cy="input-field"
v-if="type !== 'textarea'"
:disabled="disabled"
:type="type"
:placeholder="placeholder"
:readonly="readonly"
:required="required"
:autofocus="autofocus"
:debounce="debounce"
:value="valueInner"
/>
<!-- <input
ref="inputRef"
data-cy="input-field"
v-if="type !== 'textarea'"
:disabled="disabled"
:type="type"
:placeholder="placeholder"
:readonly="readonly"
:required="required"
:autofocus="autofocus"
:debounce="debounce"
v-model="valueInner"
/> -->
</template>
<script>
import { defineComponent, ref, onMounted, nextTick, watch } from "vue";
export default defineComponent({
props: {
/** HTML5 attribute */
disabled: { type: String },
/** HTML5 attribute (can also be 'textarea' in which case a `<textarea />` is rendered) */
type: { type: String, default: "text" },
/** HTML5 attribute */
placeholder: { type: String },
/** HTML5 attribute */
readonly: { type: Boolean },
/** HTML5 attribute */
required: { type: Boolean },
/** v-model */
modelValue: { type: [String, Number, Date], default: "" },
autofocus: { type: Boolean, default: false },
debounce: { type: Number, default: 1000 },
},
emits: ["update:modelValue"],
setup(props, { emit }) {
const inputRef = ref(null);
const timeout = ref(null);
const valueInner = ref(props.modelValue);
if (props.autofocus === true) {
onMounted(() => {
// I don't know we need nexttick
nextTick(() => {
inputRef.value.focus();
// setTimeout(() => inputRef.value.focus(), 500);
});
});
}
watch(valueInner, (newVal, oldVal) => {
const debounceMs = props.debounce;
if (debounceMs > 0) {
clearTimeout(timeout.value);
timeout.value = setTimeout(() => emitInput(newVal), debounceMs);
console.log(newVal);
} else {
console.log(newVal);
emitInput(newVal);
}
});
function emitInput(newVal) {
let payload = newVal;
emit("update:modelValue", payload);
}
// const onInput = (event) => {
// emit("update:modelValue", event.target.value);
// };
return { inputRef, valueInner };
},
});
</script>
App.vue
<template>
<CInput :autofocus="true" v-model.trim="inputValue1" />
<CInput :autofocus="false" v-model.trim="inputValue2" />
<pre>Input Value 1: {{ inputValue1 }}</pre>
<pre>Input Value 2: {{ inputValue2 }}</pre>
</template>
<script>
import { ref } from "vue";
import CInput from "./components/CInput.vue";
export default {
name: "App",
components: { CInput },
setup() {
const inputValue1 = ref("");
const inputValue2 = ref("");
return { inputValue1, inputValue2 };
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
CodePudding user response:
So far I have only built one App using Vue 3 Vite.
I went with the <script setup>
method only and stuck with it, this is how that looks when defining props:
<script setup>
import { onMounted } from 'vue'
const props = defineProps({
readonly: { type: Boolean },
required: { type: Boolean },
})
onMounted(() => {
console.dir(props.readonly)
})
</script>
In the Vite setup, defineProps
is globally registered, seems to be the only method that does not require an import
, I would not know if this is true of any other compiler methods.
v-model
in Vue3 pops out as modelValue
not value
, maybe you can defineProp
the modelValue
unless its there by default always?
There is some explanation here: https://v3.vuejs.org/guide/migration/v-model.html#_2-x-syntax
Hope this applies to you and helps.
CodePudding user response:
The full warning is:
[Vue warn]: Extraneous non-props attributes (modelModifiers) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.
CInput
has more than one root node (i.e., the <input>
and the comment node), so the component is rendered as a fragment. The .trim
modifier on CInput
is normally passed onto root node's v-model
, as seen in this demo. Since the actual code is a fragment, Vue can't decide for you where to pass on the modelModifiers
prop, leading to the warning you observed.
However, declaring a modelModifiers
prop to receive the modifiers is enough to resolve the problem:
// CInput.vue
export default {
props: {
modelModifiers: {
default: () => ({})
}
}
}