Vue - v-model inside a component within a component - vue.js

I'm trying to separate my project now into components to make the code readable when adjusting into a responsive app. The problem is passing the v-model from base-select -> child -> parent. How do I store the data selected to the Parent.vue items: ''? Here is my code below.
Parent.vue
<template>
<child></child>
</template>
<script>
import Child from './components/Child'
export default {
components: {
Child,
},
data: ()=> ({
item: ''
})
}
</script>
Child.vue
<template>
// Random HTML
// Random HTML 2
<base-select
:items="select"
>
</template>
<script>
import BaseSelect from '#/components/BaseSelect'
export default {
components: {
BaseSelect,
},
data: ()=> ({
select: ['Select 1', 'Select 2']
})
}
</script>
BaseSelect.vue
<template>
<v-select
v-bind="$attrs"
v-on="$listeners"
class="body-2"
solo
dense
clearable
/>
</template>

To implement v-model you need to add a value property to each child component. Each component will also need to emit an input event so that the parent component can pick up the change (read more here). Note that if you are passing data down through too many components, you should probably look at using Vuex however in this case it would probably still be fine.
Your components would have to look something like this to pass v-model all the way to the base component:
Parent.vue
<template>
<!-- Pass the data item below -->
<child v-model="item"></child>
</template>
<script>
import Child from './components/Child'
export default {
components: {
Child,
},
data: ()=> ({
item: ''
})
}
</script>
Child.vue
<template>
// Random HTML
// Random HTML 2
<base-select
:items="select"
value="value"
#input="e => $emit('input', e)"
>
</template>
<script>
import BaseSelect from '#/components/BaseSelect'
export default {
components: {
BaseSelect,
},
// We add the value prop below to work with v-model
props: {
value: String
},
data: ()=> ({
select: ['Select 1', 'Select 2']
}),
}
</script>
BaseSelect.vue
<template>
<v-select
v-bind="$attrs"
v-on="$listeners"
value="value"
#input="e => $emit('input', e)"
class="body-2"
solo
dense
clearable
/>
</template>
<script>
export default {
props: {
value: String
}
}
</script>
You can find a similar working example that I did here.

You need to use $emit (documentation) to passing data back to parent components. Or you can start using Vuex (state manager for Vue.js).
You also can check the live demo here.

Related

How to pass vmodel property to child component in nuxjs

I can't seem to pass dynamically modified properties from layouts into the <Nuxt /> component.
This is my ~/layouts/default.vue
<template>
<div>
<input v-model="myprop" />
<span>{{myprop}}</span>
<Nuxt />
</div>
</template>
<script>
export default {
provide: function () {
return {myprop: this.myprop};
},
data: () => ({
myprop: 'hello galaxy',
}),
}
</script>
This is my ~/pages/index.vue
<template>
<div>My Prop is: {{myprop}}</div>
</template>
<script>
export default {
inject: ["myprop"]
}
</script>
On the web page I see hello galaxy printed 3 times, once in the input, once in a span, and once in the Nuxt component. But when I edit the input field, only the span is updated. The Nuxt component does not capture the changes in myprop. The Nuxt component continues to show only hello galaxy while put the input and span shows changes as I type on my keyboard
What am I doing wrong?
The provide/inject is useful for simple situation, but if you've some reactive stuff the vuex store is more convenient :
in store/index.js
add a state called search and its mutations and actions :
export const state=()=>({
search:''
})
export const mutations ={
SET_SEARCH(state,payload){
state.search=payload
}
}
export const actions ={
setSearch(context,payload){
context.commit('SET_SEARCH',payload) ​
}
}
in layout/default.vue add computed property with setter/getter bound to the store:
<template>
<div>
<input v-model="search" />
<span>{{search}}</span>
<Nuxt />
</div>
</template>
<script>
export default {
computed:{
search:{
get(){
return this.$store.state.search
},
set(val){
this.$store.dispatch('setSearch',val)
}
}
}
}
</script>
in pages/index.vue :
<template>
<div>My search is: {{search}}</div>
</template>
<script>
export default {
computed:{
search(){
return this.$store.state.search
}
}
}
</script>

How to pass layout property to page component in nuxtjs

I am completely new to VueJS and NuxtJS. I can't seem to pass a property in the layout to a page component.
This is my layouts/default.vue
<template>
<Nuxt myprop="hello world" />
</template>
<script>
export default {
data: () => ({
myprop: 'hello galaxy',
}),
}
</script>
This is my pages/index.vue
<template>
<div>My Prop is: {{myprop}}</div>
</template>
<script>
export default {
props: {
myprop: {
type: String
},
},
}
</script>
When I load up my app, I expect to see My Prop is: hello world. But instead, I see My Prop is:, and it seems myprop is empty.
What am I doing wrong? How does a layout component pass a property to child component in VueJS or NuxtJS?
You cannot pass props in that way, but you could use provide/inject pattern to pass the data from layout to the page :
layout/default.vue
<template>
<Nuxt />
</template>
<script>
export default {
provide: function () {
return { myprop: this.myprop };
},
data: () => ({
myprop: 'hello galaxy',
}),
}
</script>
pages/index.vue
<template>
<div>My Prop is: {{myprop}}</div>
</template>
<script>
export default {
inject: ["myprop"],
}
</script>

Parent variable not updated when updating trough child component

I am trying to create a few custom form fields for my page and i learned that i cannot use props to do so so i am trying to find a way to update my parent component variable when i use my child component. Whe i check the parent variable it is always empty.
Here is my component:
<template>
<input
v-model="value"
:placeholder="placeHolder"
class="form-field"
>
</template>
<script>
export default {
props: ['placeHolder'],
data() {
return {
value: ''
}
},
methods: {
updateValue(){
this.$emit("update-text", this.value);
}
},
watch: {
value: function(){
this.updateValue
}
}
}
</script>
And this is how i use the component:
<TextField placeholder="Nome" :update-text="name = value"/>
what exactly am i doing wrong?
I am using vue.js with nuxt.js
I think a simpler approach in this case might be emitting an input event from your custom text field and binding the component to the variable using v-model.
TextField.vue
<template>
<input
#input="$emit('input', $event.target.value)"
:placeholder="placeHolder"
class="form-field"
>
</template>
<script>
export default {
props: ['placeHolder']
}
</script>
Usage
<template>
<TextField placeholder="Nome" v-model="name"/>
</template>
<script>
export default {
data: () => ({
name: '',
}),
}
</script>
Read more about using v-model on custom components here.

Vue.js: too many $emit from children to parent

I have the main component that containing other components which containing anothers components.
So, I have the click events in these components, but to handle it in my parent component, I need to make $emit call in each nested component.
How to make this process more simple, for example like in React, where I need just pass the function handler into component.
In vue 2.2.3+ you can use provide and inject to pass function from ancestor component to child, like great grandparent to child.
please refer following code
// app.vue
<template>
<div id="app">
<HelloWorld msg="button1" />
<HelloWorld msg="button2" />
<HelloWorld msg="button3" />
count: {{ count }}
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
provide() {
return {
clickHandler: this.clickHandler,
};
},
data() {
return {
count: 0,
};
},
components: {
HelloWorld,
},
methods: {
clickHandler() {
this.count += 1;
console.log("click received");
},
},
};
</script>
// HelloWorld.vue
<template>
<button #click="clickHandler">{{ msg }}</button>
</template>
<script>
export default {
name: "HelloWorld",
inject: ["clickHandler"],
props: {
msg: String,
},
};
</script>
you can see the same clickHandler function from parent is executed with modifying parents count prop on click of each children.
this clickHandler can be injected directly to any descendent at any level therefore application like
parent > child.1 > child.1.1 > child.1.1.1 > child.1.1.1.1(click)
the child.1.1.1.1 can be injected with clickHandler form parent.
try the code at codesandbox
also refer provide/inject
if you need the same value up in the hierarchy or anywhere in the current module, you should try to use the Vuex(State Management) library.

Vue js how to use props values to v-model

I have two component namely App.vue and hello.vue
In App component I import the hello component and use props to pass relevant data to the hello component.
there I bind data which are took from the App component.
In my hello component I have a input box bind to the passed value.
My final goal is pass values as props to the hello component and change it and finally
pass that edited values to the backend using the save method.
How do I achive this?
This is what I have done up to now.
App.vue
<template>
<div id="app">
<hello-world :msg="'hello good morning'"></hello-world>
</div>
</template>
<script>
import helloWorld from "./components/HelloWorld";
export default {
components: {
helloWorld
}
};
</script>
hello.vue
<template>
<div>
<input type="text" :value="msg">
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String
}
};
</script>
In my hello component's input field v-model is not possible. I want something similar to the v-model.
You cannot use prop to bind to v-model. Child component is not supposed to modify prop passed by the parent component.
You will have to create a copy of prop in your child component if you wish to use prop with v-model and then watch prop like this:
<template>
<div>
<input type="text" #input="onInput" v-model="msgCopy">
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String
},
data() {
return { msgCopy: '' };
},
methods: {
onInput(newInputValue) {
this.$emit('msgChange', newInputValue);
}
}
watch: {
msg(newVal) {
this.msgCopy = newVal;
}
}
};
</script>
Also, notice the use of event handler #input to pass changed prop back to the parent component via event. As a syntax sugar, you can make your Hello component work as a custom form input control by adopting to v-model lifecycle.