how to bind v-model to child component that contains input - javascript

I have some components that look like this.
<template>
<q-layout>
<v-input v-model="something" />
</q-layout>
</template>
<script>
import { QLayout } from 'quasar'
import { Input } from 'vedana'
export default {
name: 'index',
components: {
QLayout,
Input
},
data () {
return {
something: ''
}
}
}
this v-input component looks like this:
<template>
<input
:type="type ? type : 'text'"
class="v-input"/>
</template>
<script>
export default {
props: ['type'],
name: 'v-input'
}
</script>
When I enter data into the input something does not bind to whatever is in the value of the input that is inside of v-input.
How do I achieve this?

To enable the use of v-model the inner component must take a value property.
Bind the value to the inner <input> using :value, not v-model (this would mutate the prop coming from the parent). And when the inner <input> is edited, emit an input event for the parent, to update its value (input event will update the variable the parent has on v-model).
Also, if you have a default value for the type prop, declare it in props, not in the template.
Here's how your code should be
<template>
<input
:type="type"
:value="value"
#input="$emit('input', $event.target.value)"
class="v-input" />
</template>
<script>
export default {
props: {
type: {default() { return 'text'; }},
value: {} // you can also add more restrictions here
},
name: 'v-input'
}
</script>
Info about what props can have: Components / Passing data With Props.
Demo below.
Vue.component('v-input', {
template: '#v-input-template',
props: {
type: {default() { return 'text'; }},
value: {} // you can also add more restrictions here
},
name: 'v-input'
});
new Vue({
el: '#app',
data: {
something: "I'm something"
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<p>Parent something: {{ something }}</p>
<hr>
Child: <v-input v-model="something" />
</div>
<template id="v-input-template">
<input
:type="type"
:value="value"
#input="$emit('input', $event.target.value)"
class="v-input" />
</template>

https://v2.vuejs.org/v2/guide/components.html#sync-Modifier
<template>
<q-layout>
<v-input :value.sync="something" />
</q-layout>
</template>
<template>
<input
:type="type ? type : 'text'"
v-model="inputVal"
class="v-input"/>
</template>
<script>
export default {
props: ['type', 'value'],
name: 'v-input',
data:function(){
return {
inputVal: ''
};
},
watch:{
value: function(newValue){
this.$emit('update:value', newValue)
}
}
}
</script>
You need to pass your value to the input component using the .sync modifier so the changes will sync back to the parent.

Related

Vue 3: Unable to update parent data from child component checkbox

I'm trying to move a checkbox to a child component, but I can't get it to update the data stored in the parent.
Parent:
<template>
<CheckboxComponent
:value="profile.doYouAgree"
label="Do you agree?"
#input="profile.doYouAgree = $event.target.value"
/>
<div>{{ profile.doYouAgree }}</div>
</template>
<script>
import CheckboxComponent from "./components/CheckboxComponent.vue";
import { reactive } from "vue";
const profile = reactive({
name: "A Name",
email: "someone#me.com",
doYouAgree: false,
});
export default {
name: "App",
components: {
CheckboxComponent,
},
setup() {
return {
profile,
};
},
};
</script>
Child:
<template>
<div class="hello">
<label for="checkbox">{{ label }}</label>
<input
type="checkbox"
:value="modelValue"
right
#input="$emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
<script>
export default {
name: "CheckboxComponent",
props: {
label: {
type: String,
default: "",
},
modelValue: {
type: Boolean,
},
},
};
</script>
In the following sandbox, I am expecting the false to turn to true when the box is checked:
https://codesandbox.io/s/gifted-worker-vm9lyt?file=/src/App.vue
There's a couple of problems here:
$event.target.value is a string rather than a boolean. Change this to $event.target.checked
Your parent is listening to input but your child is emitting update:modelValue. Save yourself a lot of hassle and use v-model in the parent:
<CheckboxComponent
v-model="profile.doYouAgree"
label="Do you agree?"
/>

Use keyup enter on custom input field - Vue JS

I have to use keypu.enter in a custom input component on Vue. I want that the component <input /> can execute a function hosted in parent component after a enter key event during typing
This is the component code
<input v-on:keyup.enter="$emit('keyup')"/>
And there are the main page
<template>
<se-input #keyup="function()"/>
</template>
<script>
import inputField from '../components/inputfield.vue'
export default {
name: 'inputField',
components: {
'custom-input': inputField
},
},
methods: {
function () {
// Function
}
}
}
</script>
Try to pass value to your custom event, and in parent component listen to that event :
Vue.component('seInput', {
template: `
<div class="">
<input v-on:keyup.enter="$emit('keyup', $event.target.value)"/>
</div>
`
})
new Vue({
el: '#demo',
data() {
return {
inputValue: null
}
},
methods: {
handleInput(val) {
this.inputValue = val
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<h3>{{ inputValue }}</h3>
<se-input #keyup="handleInput"/>
</div>

Vue JS - How to get props value in parent component

I have three components component-1, component-2, and an App component, I pass a Boolean props from component-1 to component-2 then using #click event I change the props value from true to false and vice versa
App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" width="20%" />
<component1 />
</div>
</template>
component-1.vue
<template>
<div>
<component2 :have-banner="true" />
</div>
</template>
<script>
import component2 from "./component-2";
export default {
components: {
component2,
},
};
</script>
component-2.vue
<template>
<div>
<button #click="AssignBanner = !AssignBanner">Click me</button>
<p>{{ AssignBanner }}</p>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
haveBanner: Boolean,
},
data() {
return {
AssignBanner: this.haveBanner,
};
},
};
</script>
I want to get the value of the props in component-1, that is, I want to track the changing value in component-1, since I want to write some logic, but I can’t keep track of the value in component-1.
You can see the given code in codesandbox
Looks like you want to achieve two-way binding for the prop haveBanner. You can achieve this with the .sync modifier if you are using Vue 2.3+.
component-1.vue
<template>
<div>
<component2 :have-banner.sync="haveBanner" />
</div>
</template>
<script>
import component2 from "./component-2";
export default {
components: {
component2,
},
data() {
return {
haveBanner: true,
}
},
};
</script>
component-2.vue
<template>
<div>
<button #click="assignBanner = !assignBanner">Click me</button>
<p>{{ assignBanner }}</p>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
haveBanner: Boolean,
},
data() {
return {
assignBanner: this.haveBanner,
};
},
watch: {
assignBanner(value) {
// propagate to parent component
this.$emit('update:haveBanner', value)
},
},
};
</script>

Avoid mutating props in nested forms

Let's say I have a form divided in two sections each communicated with parent form via props. How do I avoid mutating props passed from parent form?
ParentForm.vue
<template>
<div>
<AddressForm :form-data="formData.address" />
<OtherForm :form-data="formData.other" />
</div>
</template>
<script>
export default {
data() {
return {
formData: {
address: {
address_no: "",
country: "",
},
other: {
remarks: "",
},
},
};
},
};
</script>
AddressForm.vue
<template>
<div>
<input v-model="formData.address_no" />
<input v-model="formData.country" />
</div>
</template>
<script>
export default {
name: "AddressForm",
props: { formData: { type: Object, required: true } },
};
</script>
OtherForm.vue
<template>
<div>
<textarea v-model="formData.remarks" />
</div>
</template>
<script>
export default {
name: "OtherForm",
props: { formData: { type: Object, required: true } },
};
</script>
Changing value of input fields in AddressForm.vue or OtherForm.vue will trigger a warning on props mutation. I wanted to know how do I communicate up/down with the forms without mutating a props?
I have come up with possible solutions but I don't know which one should I go for (and also how to implement it)
Clone formData prop in created() and link v-model to the local cloned formData instead of prop. Then watch the cloned formData and emit every time the value changed.
Bind using v-model instead of props
Make your child forms as custom inputs using v-model instead of passing props:
AddressForm.vue
<template>
<div>
<input :value="value.address_no" #input="$emit('input',{...value,address_no:$event.target.value)" />
<input :value="value.country" #input="$emit('input',{...value,country:$event.target.value)" />
</div>
</template>
<script>
export default {
name: "AddressForm",
props: { value: { type: Object, required: true } },
};
</script>
OtherForm :
<template>
<div>
<textarea :value="value.remarks" #input="$emit('input',{...value,remarks:$event.target.value)"/>
</div>
</template>
<script>
export default {
name: "OtherForm",
props: { value: { type: Object, required: true } },
};
</script>
in parent component :
<AddressForm v-model="formData.address" />
<OtherForm v-model="formData.other" />
You could assign in beforeMount() those props to some data values and then $emit them back.
Obviously, v-model should refer to the properties defined in data().

how to pass props correctly?

I can't pass multiple elements. How can I do this?
export default {
props: {
elem: {
type: Object,
required: true,
},
whichScreen: whichScreen
},
You can add the whichScreen prop like below :
export default {
props : {
elem : {
type: Object,
required: true,
},
whichScreen : String
},
}
you can pass props to the component like below :
<my-component :elem="{ 'key' : 'value' }" :which-screen="'Screen 1'"></my-component>
Complete Working Sample :
Vue.component('my-component', {
template: '#tmpl-my-component',
props : {
elem : {
type: Object,
required: true,
},
whichScreen : String
},
});
new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<my-component :elem="{ 'key' : 'value' }" :which-screen="'Screen 1'"></my-component>
</div>
<template id="tmpl-my-component">
<div>
<div><h4>Prop `elem` :</h4> {{elem}}</div>
<div><h4>Prop `whichScreen` :</h4> {{whichScreen}}</div>
</div>
</template>
Here is how to use props in the child component.
Parent component:-
<template>
<div id="app">
<child data="Shreekanth is here"/> // Child component and Ddta attribute passing to child component
</div>
</template>
<script>
import Child from './components/Child.vue' // Child component imported
export default {
name: 'App',
components: {
Child
}
}
</script>
Child component:-
<template>
<div>
<div>{{data}}</div> // Used data attribute used in child template
</div>
</template>
<script>
export default {
name: 'Home',
props: {
data: String // How data attribute registed in child component
}
}
</script>

Categories