Binding v-model from of child component to its parent - vue.js

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>

Related

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 data with props defined in grandparent to grandchild

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" />

VueJS best practices for passing form-data to child and back to parent

I'm working on a VueJS project and have parent/child components like this.
Parent.vue
<template>
<ChildA v-bind="this.postData.someDataA" />
...
<ChildZ v-bind="this.postData.someDataZ"/>
<button #click="save">Save</button>
</template>
<script>
import ChildA from './ChildA';
...
import ChildZ from '.ChildZ';
data() {
return {
postData: {
someDataA: {field1: 'initialValue'}
someDataB: { // no initial value'}
...
},
},
methods: {
save() {this.$root.db.save(this.postData)}
}
</script>
Child.vue
<template>
<input type="text" v-model="field1" />
...
<input type="text" v-model="field10" />
</template>
<script>
props: {
field1:{type: String, default: 'default if not set by parent'},
...
}
</script>
As you can see, I want to pass this.postData from Parent.vue to a function which saves it to a DB. However, the values for someDataA, etc.. come from the Child.vue.
When I run my code like this, I get the Vue warning:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
Now, my question is, what's the best practice to deal with this kind of situation? Do I have to implement a <ChildA #change="setSomeDataA()" /> to each of the child elements, and $emit an event each time a value of the childs props change?
Actually it's better to make computed properties instead of using copy of value prop.
props: {
value: {
type: Object,
default: () => ({})
}
},
computed: {
field1: {
get() { return this.value.field1 },
set(field1) { this.$emit('input', {...this.value, field1 })}
}
}
and use it like that
<input type="text" v-model="field1" />
Set up your child to use v-model and then emit the change. For example:
The parent calls the child using v-model
Parent.vue
<template>
<ChildA v-model="this.postData.someDataA" />
...
<ChildZ v-model="this.postData.someDataZ"/>
<button #click="save">Save</button>
</template>
<script>
import ChildA from './ChildA';
...
import ChildZ from '.ChildZ';
data() {
return {
postData: {
someDataA: {field1: 'initialValue'}
someDataB: { // no initial value'}
...
},
},
methods: {
save() {this.$root.db.save(this.postData)}
}
</script>
The child takes the prop value and sets an internal variable to value. Your input uses the internal variable as it's v-model. Then using #change or a watcher to emit('input', <your internal variable>)
Child.vue
<template>
<input type="text" #change="objChanged" v-model="myObj.field1" />
...
<input type="text" #change="objChanged" v-model="myObj.field10" />
</template>
<script>
props: {
value:{type: String, default: 'wont need a default'},
...
},
data: {
myObj: {}
},
methods: {
objChanged(){
this.$emit('input', this.myObj);
}
},
created(){
this.myObj = this.value;
}
</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>

How to get component data? How to access the components from the outside?

I derive the component:
<my-component> </my-component>
The question is how to get the properties of this particular component, knowing only its tag "my-component"?
A more detailed description of the problem:
Using the vue-cli-service build --target wc --name my-component my-component.vue command, I compile in js file
Then I connect this script in the header of the site.
Then I use the component
But how to get the properties of this component now and generally how to access this component? How to get the input field value in this component?
Sorry, I probably didn’t explain it well.
I need something like this:
<my-component id="my-component"> </my-component>
<script>
let inputValue = $("#my-component").find("input").val();//but this not work
</script>
Or
<script>
let props = <my-component></my-component>// this component props
</script>
In the my-component.vue file, this code:
<template>
<input :placeholder="test" type="text" name="test" value="">
</template>
<script>
export default {
name: 'MyInputComponent',
props: {
placeholder: {
type: String,
default: "00"
}
},
data () {
return {
}
},
}
</script>
usage
<template>
<input :placeholder="placeholder" type="text" #input="emitInputValue">
</template>
<script>
export default {
name: 'MyInputComponent',
props: {
placeholder: {
type: String,
default: "00"
}
},
methods: {
emitInputValue ($event) {
this.$emit('input', $event.target.value)
}
}
}
</script>
get value
<my-component #input"getValue"> </my-component>
methods: {
getValue (payload) {
console.log(payload)
}
}
<template>
<input :placeholder="test" :id="id" type="text" name="test" value="">
</template>
<script>
export default {
name: 'MyInputComponent',
props: {
placeholder: {
type: String,
default: "00"
},
id: {
type: String
}
},
data () {
return {
}
},
}
</script>
Now your id would be available and you can do whatever you want.
Only one suggestion i would like to give is: when you are using vue.js then try avoiding jquery for getting input value. there are many ways in vue.js itself you can get input text value.
Let me know if you need something else