Is it possible to use a prop as a v-model value? - vue.js

Is it possible to use the value of a prop as the input's v-model?
I normally do the following when creating an input:
<template>
<form>
<input v-model="form.email" type="email"/>
</form>
</template>
<script>
export default {
data() {
return {
form: {
email: '',
}
}
}
}
</script>
But now I'm trying to achieve the following where this.myProp is used within the v-model without being displayed as a string on the input:
<template>
<form>
<input v-model="this.myProp" type="email"/>
</form>
</template>
<script>
export default {
props: ['myProp'] // myProp = form.email for example (to be handled in a parent component)
}
</script>

Yes, but while using it in parent component. In child component you need to extract value and #input instead of using v-model (v-model is shortcut for value="" and #input) Here is an example of input with label, error and hint in Vue 3 composition API.
BaseInput.vue
<template>
<div class="flex flex-col">
<label>{{ label }}</label>
<input v-bind="$attrs" :placeholder="label" :value="modelValue" #input="$emit('update:modelValue', $event.target.value)">
<span v-for="item of errors" class="text-red-400">{{ item.value }}</span>
<span v-if="hint" class="text-sm">{{ hint }}</span>
</div>
</template>
<script setup>
defineProps({ label: String, modelValue: String | Number, errors: Array, hint: String })
defineEmits(['update:modelValue'])
</script>
Using v-bind="$attrs" you target where attributes like type="email" need to be applied in child component. If you don't do it, it will be added to the top level DOM element. In above scenario <div>.
ParentComponent.vue
<BaseInput type="email" v-model="formData.email" :label="Email" :errors="formErrors.email"/>

Related

Vue 3 v-model not working with custom input component in Nuxt.js

I am trying to use a custom Vue input component with a v-model, but the value is not updating in the parent component. I am using Vue 3 with Nuxt.js.
Here is my custom input component:
<template>
<input
type="text"
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)"
:placeholder="placeholder"
class="border border-gray-300 rounded-lg w-full p-2 text-black m-1"
/>
</template>
<script setup>
const props = defineProps({
modelValue: String,
placeholder: String,
});
const emit = defineEmits(["update:modelValue"]);
</script>
<script>
export default {
name: "MyInput",
};
</script>
And here is how I am using it in the parent component:
<template>
<div>
<MyInput v-model="inputValue" placeholder="Enter a value" />
</div>
</template>
<script>
import MyInput from "./MyInput.vue";
export default {
name: "MyParentComponent",
components: {
MyInput,
},
data() {
return {
inputValue: "",
};
},
};
</script>
The inputValue data property is not being updated when I type in the input field. Can someone help me figure out what I'm doing wrong?
I have a Vue 3 project without Nuxt.js using this exact same code and it works there.
there is no mistake in your codes I used exact same code and it's working with no problem
but:
there is no need to import components (Nuxt supports auto import)
your file's structure should be like this:
|__components
|
|__MyInput.vue
|
|__MyParentComponent.vue
|__app.vue
MyInput.vue
<template>
<input
type="text"
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)"
:placeholder="placeholder"
class="border border-gray-300 rounded-lg w-full p-2 text-black m-1"
/>
</template>
<script setup>
const props = defineProps({
modelValue: String,
placeholder: String,
});
const emit = defineEmits(["update:modelValue"]);
</script>
the dev tools will show the name of the component. So no additional naming is necessary.
MyComponent.vue
<template>
<div>
<MyInput v-model="inputValue" placeholder="Enter a value" />
<p>{{ inputValue }}</p>
</div>
</template>
<script setup>
let inputValue = ref("");
</script>
app.vue
<template>
<MyComponent />
</template>

Dynamic Placeholder in Vue 3 with Global Component

I am trying to set dynamic text for the placeholder attribute on my search bar. Depending on the page, I want the text in the search bar to be different (I will define it in data()).
However, since the search bar component is a global component, it doesn't seem to be editable.
(As you see below is my try, I did it with v-model based on Vue docs, however when I try with placeholder it doesn't work...)
Snippet 1 - Search bar component
<template>
<!-- Search Componenet -->
<div class="mx-5 mb-3 form-group">
<br>
<input class="mb-5 form-control" type="search" :placeholder="placeholderValue" :value="modelValue" #load="$emit('update:placeholderValue', $event.target.value)" #input="$emit('update:modelValue', $event.target.value)" />
</div>
</template>
<script>
export default {
props: ['modelValue', 'placeholderValue'],
emits: ['update:modelValue', 'update:placeholderValue']
}
</script>
Snippet 2 - Album.vue
<template>
<div class="AlbumView">
<h1>{{header}}</h1>
<h2>{{header2}}</h2>
<br>
<!-- Search Componenet -->
<SearchComponent :placeholder="placeholderValue" v-model="searchQuery" />
<!-- Dynamic Song Route Button -->
<div class="button-container-all mx-5 pb-5">
<div v-for="item in datanew" :key="item.id">
{{ item.album }}
</div>
</div>
</div>
</template>
<script>
import { datatwo } from '#/data2'
export default {
data() {
return {
placeholderValue: "Search for Albums here...",
datanew: datatwo,
searchQuery: null,
header: "Browse by Album",
header2: "Select an Album:",
publicPath: process.env.BASE_URL
};
},
}
</script>
If this is possible?
If you want to do it with v-model (the Childcomponent changes the value of the placeholder) you have to use v-model:placeholder for it to work.
And also placeholderValue is not the way to go the "Value" at the end of a prop is only needed for modelValue which is the default v-model-binding (v-model="") but if you want named v-model-binding (v-model:placeholder="") you do not want to add the "Value" in the props and emits arrays.
Example:
usage of SearchComponent
<SearchComponent :placeholder="'placeholderValue'" v-model="searchQuery" />
instead of 'placeholderValue' you can put any string you want or variable. I just put the string 'placeholderValue' as an example.
SearchComponent
<template>
<!-- Search Componenet -->
<div class="mx-5 mb-3 form-group">
<br>
<input class="mb-5 form-control" type="search" :placeholder="placeholder" :value="modelValue" #load="$emit('update:placeholderValue', $event.target.value)" #input="$emit('update:modelValue', $event.target.value)" />
</div>
</template>
<script>
export default {
name: "SearchComponent",
props: ['modelValue', 'placeholder'],
emits: ['update:modelValue'],
}
</script>
<style scoped>
</style>

Using parent prop on child function

please help me understand what I'm doing wrong here:
I have a child component that receives a prop from its parent. I want to extract part the first character of that prop value inside the child component, but I'm getting an "undefined" error. Here's the code:
Parent Component:
Child Component:
<template>
<div class="form-group">
<label>{{ label }}</label>
<div class="form-check form-check-inline">
<input
class="form-check-input"
type="text"
id="inputAgno"
v-model="aaaa"
/>
</div>
</div>
</template>
<script>
export default {
props: ["label", "value"],
computed:{
aaaa: function() {
return this.alta_establecimiento.subStr(0,4)
},
}
};
</script>
Thanks in advance!

vue how to access v-model from child component

my child component is like this
<editor v-model="edit_thread.body"></editor>
and then I access the component from inside like this
<template>
<div>
<input :value="this.value">
</div>
</template>
<script>
export default {
data() {
return {
value: this.edit_thread.body
}
}
}
</script>
not working, I miss something?
To use v-model on custom components, the component needs to:
have a prop (not a data property) named value:
<template>
<div>
<input :value="value">
</div>
</template>
<script>
export default {
props: ['value']
}
</script>
and emit an input event with new values:
<template>
<div>
<input #input="$emit('input', $event.target.value)">
</div>
</template>
demo

v-model and scoped slots not working?

I have a component:
<slot name="test" :name="name">
<input type="text" v-model="name">
</slot>
The input is bound to name in data.
When I use the slot in the parent:
<div slot="test" slot-scope="props">
<input type="text" v-model="props.name">
</div>
Data does not update on the child. It's not linked - why?
What you are seeing is actually the parent's default <input>. So you understand what I mean, add some text to both, like:
<slot name="test" :name="name">
Default: <input type="text" v-model="name">
</slot>
<div slot="test" slot-scope="props">
Actual: <input type="text" v-model="props.name">
</div>
You'll see that what appears is the default.
Now, that happens because, it seems like a bug, when the slot prop has the same name as the parent's, the slot does not work.
Workaround: rename the slot prop.
In the example below, I renamed it from name to namex. Notice the v-model in the default remains the same name because anything in the template refers to the props of that template (in other words, slot props, e.g. namex, will never be available in the parent default slot).
<slot name="test" :namex="name">
Default: <input type="text" v-model="name">
</slot>
<div slot="test" slot-scope="props">
Actual: <input type="text" v-model="props.namex">
</div>
to use v-model in scoped slots, the value of v-model must be one level deeper:
Vue.component('render-props', {
data: () => ({message: 'hello', obj: {msg: 'obj_msg'}}),
template: `<div>
<slot name="a" :message="message">
default: {{message}}
<input v-model="message"/>
</slot>
<slot name="b" :obj="obj">
default: {{obj.msg}}
<input v-model="obj.msg"/>
</slot>
</div>`
});
new Vue({
el: "#root",
template: `<div>
<render-props>
<template v-slot:a="props">
actual: {{props.message}}
<input v-model="props.message"/>
</template>
<template v-slot:b="props">
actual: {{props.obj.msg}}
<input v-model="props.obj.msg"/>
</template>
</render-props>
<cus_2 />
</div>`
});
you are not supposed to modify the data you pass to a slot, pretty
much like a prop. You should pass a method instead to change tha
value. You can always pass an object and modify a property (like a
prop) but it's also not recommended
-from https://github.com/vuejs/vue/issues/9726
So I imagine something like
<template>
<slot v-bind:value="value"></slot>
</template>
<script>
export default {
name: 'FooBar',
data() {
value: '',
},
methods: {
updateValue(e) {
this.value = e.target.value;
}
},
};
</script>
Then when using the component <FooBar> instead of using v-model, you can use the passed scoped slot props, the method (updateValue), and actual prop that will be updated, value.
<FooBar v-slot="slotProps">
<input type="text" :value="slotProps.value" #input="slotProps.updateValue" />
</Foobar>