Passing data with props defined in grandparent to grandchild - vue.js

Parent class:
<template>
<BaseButton icon="activity">Slotted Text</BaseButton>
</template>
Child class: BaseButton
<template>
<div>
{{ icon }} // returns the expected result; prop is coming in.
<slot>Default Text</slot>
<BaseIcon :name="{{icon}}" /> <-- error is here, see below.
</div>
</template>
<script>
export default {
props: {
icon: {
type: String,
required: true,
},
},
}
Grandchild:
<template>
<div class="icon-wrapper" v-html="svg"></div>
</template>
<script>
export default {
props: {
name: String
}
// code that uses the name prop
}
</script>
The error is:
Unexpected token '{' in
{{icon}}
Is there a way to pass the expression into the prop?

You should use the binding v-bind:name="icon" to bind the attribute with the property :
<BaseIcon v-bind:name="icon" />
or the shorthand :
<BaseIcon :name="icon" />

Related

Binding v-model from of child component to its parent

In my parent component I have a simple input as
<input v-model="value" />
export default class ParentComponent extends Vue {
value = "" as string;
async check() {
try {
console.log(this.value) // this works, this get the actual value of the input
…
}
}
Now I want to create a child component with that input, so I try:
<template>
<input type="text" v-model="inputValue" />
</template>
<script lang="ts">
export default {
name: “BaseTextBox",
props: {
value: {
type: String,
default: "",
},
},
};
</script>
And in the parent component:
<BaseTextBox v-model="value" />
import BaseTextBox from "#/components/_base/BaseTextBox.vue";
The component display correctly, but when I try to access to this.value in method it display as empty string because did not get modified with v-model? How can I bind it correctly?
You are using wrong value "inputValue" in v-model. It would be "value". So your input will take the prop "value" as v-model and modify that directly.
Your child component would be:
<template>
<input type="text" v-model="value" />
</template>
<script lang="ts">
export default {
name: “BaseTextBox",
props: {
value: {
type: String,
default: "",
},
},
};
</script>

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.

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

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>

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>