Vue v-model bind to Parent component input element doesn't work - vue.js

I am using Vue, Nuxt, Vue Good Table to live search a table. I create a child component named child.vue, and imported it into the parent page parent.vue. I used v-model to bind the searchTerm on the input element in the parent component. When I run it, live search doesn't work. And I got an warning from the console. Property or method "searchTerm" is not defined on the instance but referenced during render. Could you please help me out? I am new to Vue.js. Thank you
For the child.vue
<template>
<vue-good-table
:columns="columns"
:rows="rows"
:search-options="{
enabled: true,
externalQuery: searchTerm
}"
:sort-options="{
enabled: false,
}"
:group-options="{
enabled: true,
}"
/>
</template>
<script>
export default {
name: 'my-component',
data() {
return {
searchTerm: '',
columns: [xxxx]
}
}
}
</script>
For the parent.vue
<template>
<div>
<input type="text" placeholder="Live Search" v-model="searchTerm" />
</div>
<div>
<child />
</div>
</template>

Pass the prop from the parent to the child, and define searchTerm in the parents data
<template>
...
<input type="text" placeholder="Live Search" v-model="searchTerm" />
<child :searchTerm="searchTerm" />
...
</template>
<script>
data(){
return {
searchTerm: ''
}
}
...
</script>
Define the prop in the child, and remove searchTerm in the childs data
<script>
export default {
...
props: ['searchTerm'],
...
data(){
return {
//searchTerm: '' <- remove this from child
}
}
...
}
</script>
You can now access searchTerm in the childs <template> or this.$props.searchTerm in the childs <script>

Related

Vue.js: Child component omits the `ref` attribute

I have created a component called Input. All the attributes that are passed to this Input component are inherited successfully, but the ref attribute is always omitted for some reason.
I need the ref attribute to set focus on the element, after the validation fails.
Some.vue
<template>
...
<Input
type="number"
name="mobile"
ref="mobileRef"
v-model="this.form.mobile"
:class="errors.mobile ? 'error' : null"
:error="errors.mobile ?? null"
/>
...
</template>
<script>
import Input from '#Inputs/Input';
export default {
...
components: {
Input,
},
...
}
</script>
Input.vue
<template>
<input
autocomplete="none"
class="form-control"
:ref="ref"
:class="this.class"
:type="this.type ?? 'text'"
:value="modelValue"
:disabled="disabled"
#input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<script>
export default {
name: 'Input',
inheritAttrs: false,
props: ['type', 'class', 'error', 'modelValue', 'disabled', 'ref']
}
</script>
I even hardcoded the ref attribute with some static value, but the result is same, omitted?
What am I doing wrong here..?
ref is a special attribute, it's not supposed to work as a prop.
It's possible to access root element of a component as $refs.mobileRef.$el.focus(). The component can also expose all public methods explicitly, so they would be used as $refs.mobileRef.focus().
You can remove the ref prop in your Input.vue component and add a watcher inside, to watch for some errors. If there is, trigger a .focus() on your input element.
Some.vue
<template>
...
<Input
type="number"
name="mobile"
v-model="this.form.mobile"
:class="errors.mobile ? 'error' : null"
:error="errors.mobile ?? null"
/>
...
</template>
<script>
import Input from '#Inputs/Input';
export default {
...
components: {
Input,
},
...
}
</script>
Input.vue
<template>
<input
autocomplete="none"
class="form-control"
ref="mobileRef"
:class="this.class"
:type="this.type ?? 'text'"
:value="modelValue"
:disabled="disabled"
#input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<script>
export default {
name: 'Input',
inheritAttrs: false,
props: ['type', 'class', 'error', 'modelValue', 'disabled'],
watch: {
error(val) {
if (val)
this.$refs.mobileRef.focus();
},
},
}
</script>

How to pass class attribute to child component element

how can I pass class attribute from parent to child component element?
Look here:
https://codesandbox.io/s/pedantic-shamir-1wuuv
I'm adding class to the Component "Input Field"
And my goal is that the 'custom-class' will be implemented on the 'input' element in the child component
But just still using the class attribute, and not setting a new prop like "customClass" and accept it in the props of the component
Thanks!
This depends on the template structure of your ChildComponent
Your Parent Component can look like this:
<div id="app">
<InputField class="anyClass" />
</div>
If your Child looks like this:
<template>
<input ... />
</template
Because if you have only 1 root Element in your template this will inherit the classes given automatically.
if your Template e.g. looks like this: (only available in vue3)
<template>
<input v-bind="$attrs" />
<span> hi </span>
</template
You will need the v-bind="$attrs" so Vue knows where to put the attributes to. Another Solution would be giving classes as props and assigning it to the element with :class="classes"
The pattern for the customs form component in Vue 2 where the props go to a child element looks something like this.
<template>
<div class="input-field">
<label v-if="label">{{ label }}</label>
<input
:value="value"
:class="inputClass"
#input="updateValue"
v-bind="$attrs"
v-on="listeners"
/>
</div>
</template>
<script>
export default {
inheritAttrs: false,
props: {
label: {
type: String,
default: "",
},
value: [String, Number],
inputClass: {
type: String,
default: "",
},
},
computed: {
listeners() {
return {
...this.$listeners,
input: this.updateValue,
};
},
},
methods: {
updateValue(event) {
this.$emit("input", event.target.value);
},
},
};
</script>
The usage of those components could look something like this.
```html
<template>
<div id="app">
<InputField
v-model="name"
label="What is your name?"
type="text"
class="custom"
inputClass="input-custom"
/>
<p>{{ name }}</p>
</div>
</template>
<script>
import InputField from "./components/InputField";
export default {
name: "App",
components: {
InputField,
},
data() {
return {
name: "",
};
},
};
</script>
A demo is available here
https://codesandbox.io/s/vue-2-custom-input-field-4vldv?file=/src/components/InputField.vue
You need to use vue props . Like this:
child component:
<template>
<div>
<input :class="className" type="text" value="Test" v-bind="$attrs" />
</div>
</template>
<script>
export default {
props:{
className:{
type:String,
default:'',
}
}
};
</script>
Parent component:
<div id="app">
<InputField :class-name="'custom-class'" />
</div>
Since you're using vue2 the v-bind=$attrs is being hooked to the root element of your component the <div> tag. Check the docs. You can put this wrapper on the parent element if you need it or just get rid of it.
Here is a working example
Another approach
There is also the idea of taking the classes from the parent and passing it to the child component with a ref after the component is mounted
Parent Element:
<template>
<div id="app">
<InputField class="custom-class" ref="inputField" />
</div>
</template>
<script>
import InputField from "./components/InputField";
export default {
name: "App",
components: {
InputField,
},
mounted() {
const inputField = this.$refs.inputField;
const classes = inputField.$el.getAttribute("class");
inputField.setClasses(classes);
},
};
</script>
Child Element:
<template>
<div>
<input type="text" value="Test" :class="classes" />
</div>
</template>
<script>
export default {
data() {
return {
classes: "",
};
},
methods: {
setClasses: function (classes) {
this.classes = classes;
},
},
};
</script>
Here a working example
In Vue2, the child's root element receives the classes.
If you need to pass them to a specific element of your child component, you can read those classes from the root and set them to a specific element.
Example of a vue child component:
<template>
<div ref="root">
<img ref="img" v-bind="$attrs" v-on="$listeners" :class="classes"/>
</div>
</template>
export default {
data() {
return {
classes: null,
}
},
mounted() {
this.classes = this.$refs.root.getAttribute("class")
this.$refs.root.removeAttribute("class")
},
}
Pretty much it.

Passing v-model to Child Component

I'm using Vue 3. In the example below I have 3 simple components, Name Component, Phone Component and Submit Component. How do I get in the respective properties of the Submit Component, what was entered in the inputs?
Name Component:
<template>
<div>
<input type="text" v-bind="name">
</div>
</template>
Phone Component:
<template>
<div>
<input type="text" v-bind="phone">
</div>
</template>
Submit Component:
<template>
<name></name>
<phone></phone>
</template>
<script>
export default {
data() {
return {
name: '',
phone: '',
}
}
}
</script>
<template>
<section>
<childComponent ref="nameOfRef" />
</section>
</template>
methods: {
submit() {
let Data = this.$refs.nameOfRef.$data; // nameOfRef.data or nameOfRef.$data or nameOfRef.phone or nameOfRef.name
}
},
Check I think it will help to catch your parent component data.

Can't pass checked value to parent component from custom checkbox component

In my custom checkbox component, I'm trying to pass the value of the checkbox form field to my parent component:
<template>
<div class="custom-checkbox">
<div :class="{ 'bg-white': value }">
<input
:id="checkboxId"
type="checkbox"
:checked="value"
v-model="value"
#change="$emit('input', $event.target.checked)"
/>
</div>
<label :for="checkboxId">
<slot />
</label>
</div>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
default: false
},
checkboxId: {
type: String,
default: "checkbox-1"
}
}
};
</script>
Getting this error:
[Vue warn]: Avoid mutating a prop directly since the value will be
overwritten whenever the parent component re-renders. Instead, use a
data or computed property based on the prop's value. Prop being
mutated: "value"
I tried to add:
data() {
return {
checkedValue: this.value
};
}
... then replace v-model="value" with v-model="checkedValue" but the checkbox doesn't check anymore and I still don't get the value of it in parent component.
Any suggestion?
It's because you are still directly mutating value, it does not matter if you catch the #change event or not.
Try creating a computed component with a getter/setter in your child component.
computed: {
checked: {
get() {
return this.value;
},
set(value) {
this.$emit("input", value);
}
}
}
Use checked as your checkbox v-model. No need to bind anything to :checked, only v-model will suffice.
You can pass the value using v-model to this component in the parent.
For reference: https://v2.vuejs.org/v2/guide/forms.html
For the record.
CustomCheckbox.vue
<template>
<input type="checkbox" :checked="value" #change="$emit('input', $event.target.checked)">
</template>
<script>
export default {
props: ["value"]
};
</script>
Parent.vue
<template>
<div id="app">
<custom-checkbox v-model="checked"></custom-checkbox>
</div>
</template>
<script>
import CustomCheckbox from "./components/CustomCheckbox";
export default {
data: {
checked: true
},
components: {
CustomCheckbox
}
};
</script>

Putting value in a props from child to parent in vue.js is Error

Good day,
I'm trying to pass a value from child to parent using the props but I'm having an error that says,
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "search"
Here are my sample codes:
Parent component
<template>
<div>
<child :search="search" />
</div>
</template>
<script>
export default(){
data: ()=>({
search: ''
})
}
</script>
Child component
<template>
<div>
<input type="text" v-model="search" />
</div>
</template>
<script>
export default{
props: ['search']
watch:{
search(val){
this.$emit('search', val);
}
}
}
</script>
I hope somebody can help me.
You're binding v-model to search. So if you type into your input vue to mutates the property. And it's just a warning which says that the data will be lost on next update, because then the property will passed again and your last value will be overwritten.
You could do something like this:
Child:
<template>
<div>
<input type="text" v-model="search" />
</div>
</template>
<script>
export default{
mounted () {
this.searchForResult= this.search;
},
props: ['search'],
data() {
searchForResult: ''
},
watch:{
searchForResult(val){
this.$emit('search', val);
}
}
}
</script>
Parent:
<template>
<div>
<child :search="search" />
</div>
</template>
<script>
export default(){
data: ()=>({
search: ''
})
}
</script>
There is also the possibility to add a search method which you can call from parent.
Parent component
<template>
<div>
<div>received: {{childResult}}
<child :search="search" #change="change" />
</div>
</template>
<script>
export default(){
data(){
return {
search: 'some content',
childResult: ''
}
},
methods:{
change(data){
this.childResult = data;
}
}
}
</script>
Child component
<template>
<div>
<input type="text" v-model="result" />
</div>
</template>
<script>
export default{
props: {
search: String
},
data(){
return {
result: this.search
};
},
watch:{
result(val){
this.$emit('change', val);
}
}
}
</script>