How do I use v-model on none form-related elements? - vue.js

Before anything else, I'm using Vuetify's VSwitch component inside my-component. I want to return the value of my-component to the parent.
something like <my-component v-model="returnedData"></my-component>
Then the inside <my-component></my-component>
<template>
<div>
<v-switch v-model="toggledData" value="John"></v-switch>
<v-switch v-model="toggledData" value="Andrew"></v-switch>
<v-switch v-model="toggledData" value="Melissa"></v-switch>
<v-switch v-model="toggledData" value="Elizabeth"></v-switch>
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
toggledData: []
}
}
}
</script>
I want to return the value of toggledData to the parent that's using it if possible. I've been browsing the net for a while and I've been seeing only with inputs. But it was possible to some of Vuetify's components like the VTreeviewso I was thinking maybe it's possible.

Using v-model like in your example:
<my-component v-model="returnedData"></my-component>
is (by default) equivalent to this:
<my-component :value="returnedData" #input="returnedData = $event"></my-component>
Any component can support v-model just by having a value prop and emitting an input event. The names of the prop and event can be changed using the model option, see https://v2.vuejs.org/v2/guide/components-custom-events.html#Customizing-Component-v-model
All of this assumes that you want two-way data binding. In your question you seem to imply that you just want to pass data up to the parent, which is only one way. For that you only need to emit an event and listen for that event using an # listener.
Genuinely creating a two-way data binding would be tricky in this case. The easiest way is to drop the v-model on the v-switch and use the prop and event separately. There are alternatives, such as using v-model with a computed property that has a getter and setter, but I'm not convinced that would make things any clearer.

The parent component listens to the child's changes with the childToParent and if there is any change I call the childChanged () method
// Parent Component
<template>
<div id="parent">
<p>{{parentToggledData}}</p>
<Child v-on:childToParent="childChanged"/>
</div>
</template>
<script>
import Child from "./Child";
export default {
name: "parent",
components: {
Child
},
data() {
return {
parentToggledData: []
};
},
methods: {
childChanged(value) {
this.parentToggledData = value;
}
}
};
</script>
I listen to the changes on each v-switch, and if there is one, I call the emitToParent () method in this method, I send the changes to the parent with $emit which takes as parameter the event listened by the parent childToParent and "the value to send this.toggledData
// Child Component
<template>
<div id="child">
<v-switch v-model="toggledData" value="John" #change="emitToParent"></v-switch>
<v-switch v-model="toggledData" value="Andrew" #change="emitToParent"></v-switch>
<v-switch v-model="toggledData" value="Melissa" #change="emitToParent"></v-switch>
<v-switch v-model="toggledData" value="Elizabeth" #change="emitToParent"></v-switch>
</div>
</template>
<script>
export default {
name: "child",
data() {
return {
toggledData: []
};
},
methods: {
emitToParent(event) {
console.log(event)
console.log(this.toggledData)
this.$emit("childToParent", this.toggledData);
}
}
};
</script>

You can do something like this:
<v-switch #change="$emit('swithValue', value)" value="John"></v-switch>
Then in your parent component just listen for swithcValue like #switchValue="myFunction" Your function implicitly gets the emitted value and you can do with it as you wish.

Related

multi-layer deep v-model data-binding in vue3

Suppose I have two components, one being just a simple input:
<!-- Child.vue -->
<template>
<input v-model="value" />
</template>
Now suppose I have a parent element, and I would like to v-model bind the child input's value.
Pseudo-code:
<!-- Form.vue -->
<template>
<Child v-model:value="parentVariable"/>
</template>
So that way the value of the Child's input lives outside of the state of the Child component. (Or it is present in both and are binded together.)
The question is how do I two-way data-bind a variable in a parent component, to an input in the child's component?
(I am using vue3)
(If there is a best practice for this, please inform me.)
A solution is documented in the docs
you can use emit and props to propagate v-model to the parent component
Example from docs:
app.component('custom-input', {
props: ['modelValue'],
emits: ['update:modelValue'],
template: `
<input v-model="value">
`,
computed: {
value: {
get() {
return this.modelValue
},
set(value) {
this.$emit('update:modelValue', value)
}
}
}
})

Calling function inside child component without an event?

Currently trying to use a method belonging to the parent
<p class="message-date text-center">
{{ $emit('format_date_day_month_year_time', message.date) }}
</p>
However I am getting the error.
Converting circular structure to JSON
--> starting at object with constructor 'Object'
How can I call a function inside a child component that does not rely on an event? I apologize for asking such a simple question but everything I was able to find on google is using $emit and using an event.
$emit was designed to only trigger an event on the current instance of vue. Therefore, it is not possible to receive data from another component this way.
For your case, I would suggest to use Mixins especially if you need to use certain functions among multiple vue components.
Alternately, let the child component call the the parent through $emit then receive the result from the parent through a prop.
Your code could be something as follows:
Child component
<template>
<p class="message-date text-center">
{{ date }}
</p>
</template>
<script>
export default {
name: 'Child',
props: {
date: String,
},
mounted() {
this.$emit("format-date", message.date);
},
}
</script>
Parent component
<template>
<Child :date="childDate" #format-date="formatChildDate" />
</template>
<script>
import Child from '#/components/Child';
export default {
components: {
Child,
},
data: () => ({
childDate: '',
}),
methods: {
formatChildDate(date) {
this.childDate = this.formatDateDayMonthYearTime(date)
},
formatDateDayMonthYearTime(date) {
//return the formatted date
},
},
}
</script>
with $emit you call a function where the Parent can listento.
where you are using it i would suggest a computed prop of the function.
But back to your Question here is a example of emiting and listen.
//Parent
<template>
<MyComponent #childFunction="doSomethingInParent($event)"/>
</template>
//Child
<template>
<button #click="emitStuff">
</template>
.....
methods:{
emitStuff(){
this.$emit(childFunction, somedata)
}
with the event you can give Data informations to a Parentcomponent.

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.

Using v-model and refs in a slot in Vue2

I have a component that takes a main <slot> from a form that is generated elsewhere in my application. I'm trying to use v-model on the form inputs but my vue component just spits out a warning about the properties not being defined, when in fact they are.
I admit it's a weird way of doing things, but it seems to be the easiest way for me to do this since my form is being generated by Symfony.
html:
<my-component>
<input ref="start" v-model="start"/>
</my-component>
my component:
<script>
export default {
data() {
start: null
},
mounted() {
console.log(this.$refs) // === {}; expected {"start":{...}}
}
}
</script>
<template>
<div>
<slot/>
... other stuff here
</div>
</template>
console log:
Property or method "start" is not defined on the instance but referenced during render
I cannot use $refs or v-model in the html. Am I doing something wrong? Or is this just not possible.
If you declare v-model="start" in the parent then it belongs to the parent and needs to be declared there. It looks like instead you declare it in the component instead as null.
If you reorder things it should work as you expect:
Parent:
<parent>
<input v-model="start" :start="start"/>
</parent>
<script>
export default {
data() {
start: null // Important to define start here if it's used in this component's html
}
}
</script>
Component:
<template>
<div>
<slot/>
... other stuff here
</div>
</template>
<script>
export default {
props: ['start'], // Receive the prop from the parent
data() {
},
mounted () {
console.log(this.start) // Should echo the value of the prop from the parent
}
}
</script>

How to get child component's data in VueJs?

This is an easy question but I dont know the best way to do it correctly with Vue2:
Parent Component:
<template>
<div class="parent">
<child></child>
{{value}} //How to get it
</div>
</template>
Child Component:
<template>
<div class="child">
<p>This {{value}} is 123</p>
</div>
</template>
<script>
export default {
data: function () {
return { value: 123 }
}
}
</script>
Some ways you can achieve this include:
You can access the child VM directly via a ref, but this gets hairy because the ref won't be populated until the child component has mounted, and $refs isn't reactive, meaning you'd have to do something hacky like this:
<template>
<div class="parent">
<child ref="child"></child>
<template v-if="mounted">{{ $refs.child.value }}</template>
</div>
</template>
data() {
return {
mounted: false
}
},
mounted() {
this.mounted = true;
}
$emit the value from within the child component so that the parent can obtain the value when it is ready or whenever it changes.
Use Vuex (or similar principles) to share state across components.
Redesign the components so that the data you want is owned by the parent component and is passed down to the child via a prop binding.
(Advanced) Use portal-vue plugin when you want to have fragments of a component's template to exist elsewhere in the DOM.
Child components pass data back up the hierarchy by emitting events.
For example, in the parent, listen for an event and add the child value to a local one (an array for multiple child elements would be prudent), eg
<child #ready="childVal => { value.push(childVal) }"></child>
and in the child component, $emit the event on creation / mounting, eg
mounted () {
this.$emit('ready', this.value)
}
Demo ~ https://jsfiddle.net/p2jojsrn/