Vue - Access prop in child component, regular javascript - javascript

I am loading a Vue component from another Vue component and am passing a property to that component. I need to access this property in the regular javascript of that component, but cannot figure out how to do this.
The simplified parent component could look as follows:
<template>
<div>
<MenuEdit :menu-list="menuList"></MenuEdit>
</div>
</template>
<script>
import MenuEdit from '#/components/MenuEdit';
export default {
name: 'Admin',
data: function () {
return {
menuList: ["Item1","Item2","Item3","Item4"]
};
},
components: {
MenuEdit
}
}
</script>
<style scoped>
</style>
And the MenuEdit could look as follows:
<template>
<div>
{{ menuList }}
</div>
</template>
<script>
//console.log(this.menuList) // Does not work.
export default {
name: 'MenuEdit',
props: [
'menuList'
],
methods: {
testMenu: function() {
console.log(this.menuList) //This works fine
}
}
}
</script>
<style scoped>
</style>
EDIT
To add some context to the question, I am implementing sortablejs on Buefy using the following example: https://buefy.org/extensions/sortablejs
Instead of calling "vnode.context.$buefy.toast.open(Moved ${item} from row ${evt.oldIndex + 1} to ${evt.newIndex + 1})" at the end of the first const, I want to update the component (or better said, update the related Array).
In the example, the const are defined outside of the component, which is why I ended up with this question.

You cannot access the prop as that code (where your console.log is) runs before the component is mounted, before it's even declared really
If you want to access stuff when the component is first mounted, you can use the mounted lifecycle method

Related

Pass data attribute to vue 3 root instance [duplicate]

I am terribly new to Vue, so forgive me if my terminology is off. I have a .NET Core MVC project with small, separate vue pages. On my current page, I return a view from the controller that just has:
#model long;
<div id="faq-category" v-bind:faqCategoryId="#Model"></div>
#section Scripts {
<script src="~/scripts/js/faqCategory.js"></script>
}
Where I send in the id of the item this page will go grab and create the edit form for. faqCategory.js is the compiled vue app. I need to pass in the long parameter to the vue app on initialization, so it can go fetch the full object. I mount it with a main.ts like:
import { createApp } from 'vue'
import FaqCategoryPage from './FaqCategoryPage.vue'
createApp(FaqCategoryPage)
.mount('#faq-category');
How can I get my faqCategoryId into my vue app to kick off the initialization and load the object? My v-bind attempt seems to not work - I have a #Prop(Number) readonly faqCategoryId: number = 0; on the vue component, but it is always 0.
My FaqCategoryPAge.vue script is simply:
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { Prop } from 'vue-property-decorator'
import Card from "#/Card.vue";
import axios from "axios";
import FaqCategory from "../shared/FaqCategory";
#Options({
components: {
Card,
},
})
export default class FaqCategoryPage extends Vue {
#Prop(Number) readonly faqCategoryId: number = 0;
mounted() {
console.log(this.faqCategoryId);
}
}
</script>
It seems passing props to root instance vie attributes placed on element the app is mounting on is not supported
You can solve it using data- attributes easily
Vue 2
const mountEl = document.querySelector("#app");
new Vue({
propsData: { ...mountEl.dataset },
props: ["message"]
}).$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" data-message="Hello from HTML">
{{ message }}
</div>
Vue 3
const mountEl = document.querySelector("#app");
Vue.createApp({
props: ["message"]
}, { ...mountEl.dataset }).mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.js"></script>
<div id="app" data-message="Hello from HTML">
{{ message }}
</div>
Biggest disadvantage of this is that everything taken from data- attributes is a string so if your component expects something else (Number, Boolean etc) you need to make conversion yourself.
One more option of course is pushing your component one level down. As long as you use v-bind (:counter), proper JS type is passed into the component:
Vue.createApp({
components: {
MyComponent: {
props: {
message: String,
counter: Number
},
template: '<div> {{ message }} (counter: {{ counter }}) </div>'
}
},
}).mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.js"></script>
<div id="app">
<my-component :message="'Hello from HTML'" :counter="10" />
</div>
Just an idea (not a real problem)
Not really sure but it can be a problem with Props casing
HTML attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase. That means when you're using in-DOM templates, camelCased prop names need to use their kebab-cased (hyphen-delimited) equivalents
Try to change your MVC view into this:
<div id="faq-category" v-bind:faq-category-id="#Model"></div>
Further to Michal Levý's answer regarding Vue 3, you can also implement that pattern with a Single File Component:
app.html
<div id="app" data-message="My Message"/>
app.js
import { createApp } from 'vue';
import MyComponent from './my-component.vue';
const mountEl = document.querySelector("#app");
Vue.createApp(MyComponent, { ...mountEl.dataset }).mount("#app");
my-component.vue
<template>
{{ message }}
</template>
<script>
export default {
props: {
message: String
}
};
</script>
Or you could even grab data from anywhere on the parent HTML page, eg:
app.html
<h1>My Message</h1>
<div id="app"/>
app.js
import { createApp } from 'vue';
import MyComponent from './my-component.vue';
const message = document.querySelector('h1').innerText;
Vue.createApp(MyComponent, { message }).mount("#app");
my-component.vue
<template>
{{ message }}
</template>
<script>
export default {
props: {
message: String
}
};
</script>
To answer TheStoryCoder's question: you would need to use a data prop. My answers above demonstrate how to pass a value from the parent DOM to the Vue app when it is mounted. If you wanted to then change the value of message after it was mounted, you would need to do something like this (I've called the data prop myMessage for clarity, but you could also just use the same prop name message):
<template>
{{ myMessage }}
<button #click="myMessage = 'foo'">Foo me</button>
</template>
<script>
export default {
props: {
message: String
},
data() {
return {
myMessage: this.message
}
}
};
</script>
So I'm not at all familiar with .NET and what model does, but Vue will treat the DOM element as a placeholder only and it does not extend to it the same functionality as the components within the app have.
so v-bind is not going to work, even without the value being reactive, the option is not there to do it.
you could try a hack to access the value and assign to a data such as...
const app = Vue.createApp({
data(){
return {
faqCategoryId: null
}
},
mounted() {
const props = ["faqCategoryId"]
const el = this.$el.parentElement;
props.forEach((key) => {
const val = el.getAttribute(key);
if(val !== null) this[key] = (val);
})
}
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app" faqCategoryId="12">
<h1>Faq Category Id: {{faqCategoryId}}</h1>
</div>
where you get the value from the html dom element, and assign to a data. The reason I'm suggesting data instead of props is that props are setup to be write only, so you wouldn't be able to override them, so instead I've used a variable props to define the props to look for in the dom element.
Another option
is to use inject/provide
it's easier to just use js to provide the variable, but assuming you want to use this in an mvc framework, so that it is managed through the view only. In addition, you can make it simpler by picking the exact attributes you want to pass to the application, but this provides a better "framework" for reuse.
const mount = ($el) => {
const app = Vue.createApp({
inject: {
faqCategoryId: {
default: 'optional'
},
},
})
const el = document.querySelector($el)
Object.keys(app._component.inject).forEach(key => {
if (el.getAttribute(key) !== null) {
app.provide(key, el.getAttribute(key))
}
})
app.mount('#app')
}
mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app" faqCategoryId="66">
<h1>Faq Category Id: {{faqCategoryId}}</h1>
</div>
As i tried in the following example
https://codepen.io/boussadjra/pen/vYGvXvq
you could do :
mounted() {
console.log(this.$el.parentElement.getAttribute("faqCategoryId"));
}
All other answers might be valid, but for Vue 3 the simple way is here:
import {createApp} from 'vue'
import rootComponent from './app.vue'
let rootProps = {};
createApp(rootComponent, rootProps)
.mount('#somewhere')

Mixin for destroyed Vue component is still listening for events

I have a parent component that conditionally renders one of two child components:
<template>
<div>
<!-- other code that changes conditional rendering -->
<folders v-if="isSearchingInFolders" :key="1234"></folders>
<snippets v-if="!isSearchingInFolders" :key="5678"></snippets>
</div>
</template>
Each of these components use the same mixin (searchMixin) locally like so:
<template>
<div>
<div>
<snippet
v-for="item in items"
:snippet="item"
:key="item.id">
</snippet>
<img v-if="busy" src="/icons/loader-grey.svg" width="50">
</div>
<button #click="getItems">Get More</button>
</div>
</template>
<script>
import searchMixin from './mixins/searchMixin';
import Snippet from './snippet';
export default {
components: { Snippet },
mixins: [searchMixin],
data() {
return {
resourceName: 'snippets'
}
},
}
</script>
Each of the components is functionally equivalent with some slightly different markup, so for the purposes of this example Folders can be substituted with Snippets and vice versa.
The mixin I am using looks like this (simplified):
import axios from 'axios'
import { EventBus } from '../event-bus';
export default {
data() {
return {
hasMoreItems: true,
busy: false,
items: []
}
},
created() {
EventBus.$on('search', this.getItems)
this.getItems();
},
destroyed() {
this.$store.commit('resetSearchParams')
},
computed: {
endpoint() {
return `/${this.resourceName}/search`
},
busyOrMaximum() {
return this.busy || !this.hasMoreItems;
}
},
methods: {
getItems(reset = false) {
<!-- get the items and add them to this.items -->
}
}
}
In the parent component when I toggle the rendering by changing the isSearchingInFolders variable the expected component is destroyed and removed from the DOM (I have checked this by logging from the destroyed() lifecycle hook. However the searchMixin that was included in that component does not appear to be destroyed and still appears to listen for events. This means that when the EventBus.$on('search', this.getItems) line is triggered after changing which component is actively rendered from the parent, this.getItems() is triggered twice. Once for folders and once for snippets!
I was expecting the mixins for components to be destroyed along with the components themselves. Have I misunderstood how component destruction works?
Yes, when you pass an event handler as you do EventBus keeps the reference to the function you passed into. That prevents the destruction of the component object. So you need clear the reference by calling EventBus.$off so that the component can be destructed. So your destroy event hook should look like this:
destroyed() {
this.$store.commit('resetSearchParams')
EventBus.$off('search', this.getItems)
},

How to throw data to main App.vue from views? [duplicate]

I have two components:
App.vue
Sidekick.vue
In my App.vue component, I have a property that I would like to access from Sidekick.vue
App.vue
<template>
<div id="app">
<p>{{ myData }}</p>
<div class="sidebar">
<router-view/> // our sidekick component is shown here
</div>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
myData: 'is just this string'
}
}
}
</script>
Sidekick.vue
<template>
<div class="sidekick">
{{ myData }}
</div>
</template>
<script>
export default {
name: 'Sidekick'
}
</script>
I would like access to myData (which is declared in App.vue) from Sidekick.vue
I have tried importing App.vue from within Sidekick.vue by doing something like:
Sidekick.vue (incorrect attempt)
<script>
import App from '#/App'
export default {
name: 'Sidekick',
data () {
return {
myData: App.myData
}
}
}
</script>
I have read about props - but have only seen references to child / parent components. In my case, Sidekick.vue is shown in a div inside App.vue (not sure if this makes it a "child"). Do I need to give access of myData to <router-view/> somehow?
UPDATE: (to show relationship between App.vue and Sidekick.vue
index.js (router file)
import Vue from 'vue'
import Router from 'vue-router'
import Sidekick from '#/components/Sidekick',
import FakeComponent from '#/components/FakeComponent'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
redirect: '/fakecomponent'
},
{
path: '/sidekick',
name: 'Sidekick',
component: Sidekick
},
{
path: '/fakecomponent',
name: 'FakeComponent',
component: FakeComponent
}
]
})
export default router
Sidekick.vue gets rendered when we hit /sidekick
Just keep in mind, the rule of thumb is using props to pass data in a one-way flow
props down, events up.
https://v2.vuejs.org/v2/guide/components.html#Composing-Components
Quick solution:
Global event bus to post messages between your <App/> and <Sidekick/> components.
https://v2.vuejs.org/v2/guide/components.html#Non-Parent-Child-Communication
Long term solution:
Use a state management library like vuex to better encapsulates data in one place (a global store) and subscribe it from your components tree using import { mapState, mapMutations } from 'vuex'
When you have parent-child communication, the best and recommended
option is to use props and events. Read more in Vue docs
When want to have shared state between many components the best and
recommended way is to use Vuex.
If you want to use simple data sharing you can use Vue observable.
Simple example: Say that you have a game and you want the errors to be accessible by many components. (components can access it and manipulate it).
errors.js
import Vue from "vue";
export const errors = Vue.observable({ count: 0 });
Component1.vue
import { errors } from 'path-of-errors.js'
export default {
computed: {
errors () {
get () { return errors.count },
set (val) { errors.count = val }
}
}
}
In Component1 the errors.count is reactive. So if as a template you have:
<template>
<div>
Errors: {{ errors }}
<button #click="errors++">Increase</button>
</div>
</template>
While you click the Increase button, you will see the errors increasing.
As you might expect, when you import the errors.js in another component, then both components can participate on manipulating the errors.count.
Note: Even though you might use the Vue.observable API for simple data sharing you should be aware that this is a very powerful API. For example read Using Vue Observables as a State Store
App.vue:
<router-view pass_data='myData'/>
Sidekick.vue:
export default {
name: "Sidekick",
props: ["pass_data"],
created() {
alert("pass_data: "+this.pass_data)
}
}
If App.js(Parent) and Sidekick(Child)
App.js
in Template
In script
import Sidekick from './Sidekick.vue:
Sidekick.vue
props: ['myData']
now you can access myData anywhere in sidekick.
In template myData and
in scripts this.myData

Vuejs Share Data Between Components

I have two components:
App.vue
Sidekick.vue
In my App.vue component, I have a property that I would like to access from Sidekick.vue
App.vue
<template>
<div id="app">
<p>{{ myData }}</p>
<div class="sidebar">
<router-view/> // our sidekick component is shown here
</div>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
myData: 'is just this string'
}
}
}
</script>
Sidekick.vue
<template>
<div class="sidekick">
{{ myData }}
</div>
</template>
<script>
export default {
name: 'Sidekick'
}
</script>
I would like access to myData (which is declared in App.vue) from Sidekick.vue
I have tried importing App.vue from within Sidekick.vue by doing something like:
Sidekick.vue (incorrect attempt)
<script>
import App from '#/App'
export default {
name: 'Sidekick',
data () {
return {
myData: App.myData
}
}
}
</script>
I have read about props - but have only seen references to child / parent components. In my case, Sidekick.vue is shown in a div inside App.vue (not sure if this makes it a "child"). Do I need to give access of myData to <router-view/> somehow?
UPDATE: (to show relationship between App.vue and Sidekick.vue
index.js (router file)
import Vue from 'vue'
import Router from 'vue-router'
import Sidekick from '#/components/Sidekick',
import FakeComponent from '#/components/FakeComponent'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
redirect: '/fakecomponent'
},
{
path: '/sidekick',
name: 'Sidekick',
component: Sidekick
},
{
path: '/fakecomponent',
name: 'FakeComponent',
component: FakeComponent
}
]
})
export default router
Sidekick.vue gets rendered when we hit /sidekick
Just keep in mind, the rule of thumb is using props to pass data in a one-way flow
props down, events up.
https://v2.vuejs.org/v2/guide/components.html#Composing-Components
Quick solution:
Global event bus to post messages between your <App/> and <Sidekick/> components.
https://v2.vuejs.org/v2/guide/components.html#Non-Parent-Child-Communication
Long term solution:
Use a state management library like vuex to better encapsulates data in one place (a global store) and subscribe it from your components tree using import { mapState, mapMutations } from 'vuex'
When you have parent-child communication, the best and recommended
option is to use props and events. Read more in Vue docs
When want to have shared state between many components the best and
recommended way is to use Vuex.
If you want to use simple data sharing you can use Vue observable.
Simple example: Say that you have a game and you want the errors to be accessible by many components. (components can access it and manipulate it).
errors.js
import Vue from "vue";
export const errors = Vue.observable({ count: 0 });
Component1.vue
import { errors } from 'path-of-errors.js'
export default {
computed: {
errors () {
get () { return errors.count },
set (val) { errors.count = val }
}
}
}
In Component1 the errors.count is reactive. So if as a template you have:
<template>
<div>
Errors: {{ errors }}
<button #click="errors++">Increase</button>
</div>
</template>
While you click the Increase button, you will see the errors increasing.
As you might expect, when you import the errors.js in another component, then both components can participate on manipulating the errors.count.
Note: Even though you might use the Vue.observable API for simple data sharing you should be aware that this is a very powerful API. For example read Using Vue Observables as a State Store
App.vue:
<router-view pass_data='myData'/>
Sidekick.vue:
export default {
name: "Sidekick",
props: ["pass_data"],
created() {
alert("pass_data: "+this.pass_data)
}
}
If App.js(Parent) and Sidekick(Child)
App.js
in Template
In script
import Sidekick from './Sidekick.vue:
Sidekick.vue
props: ['myData']
now you can access myData anywhere in sidekick.
In template myData and
in scripts this.myData

vuejs update parent data from child component

I'm starting to play with vuejs (2.0).
I built a simple page with one component in it.
The page has one Vue instance with data.
On that page I registered and added the component to html.
The component has one input[type=text]. I want that value to reflect on the parent (main Vue instance).
How do I correctly update the component's parent data?
Passing a bound prop from the parent is not good and throws some warnings to the console. They have something in their doc but it is not working.
Two-way binding has been deprecated in Vue 2.0 in favor of using a more event-driven architecture. In general, a child should not mutate its props. Rather, it should $emit events and let the parent respond to those events.
In your specific case, you could use a custom component with v-model. This is a special syntax which allows for something close to two-way binding, but is actually a shorthand for the event-driven architecture described above. You can read about it here -> https://v2.vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events.
Here's a simple example:
Vue.component('child', {
template: '#child',
//The child has a prop named 'value'. v-model will automatically bind to this prop
props: ['value'],
methods: {
updateValue: function (value) {
this.$emit('input', value);
}
}
});
new Vue({
el: '#app',
data: {
parentValue: 'hello'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<p>Parent value: {{parentValue}}</p>
<child v-model="parentValue"></child>
</div>
<template id="child">
<input type="text" v-bind:value="value" v-on:input="updateValue($event.target.value)">
</template>
The docs state that
<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>
is equivalent to
<custom-input v-model="something"></custom-input>
That is why the prop on the child needs to be named value, and why the child needs to $emit an event named input.
In child component:
this.$emit('eventname', this.variable)
In parent component:
<component #eventname="updateparent"></component>
methods: {
updateparent(variable) {
this.parentvariable = variable
}
}
From the documentation:
In Vue.js, the parent-child component relationship can be summarized as props down, events up. The parent passes data down to the child via props, and the child sends messages to the parent via events. Let’s see how they work next.
How to pass props
Following is the code to pass props to a child element:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
How to emit event
HTML:
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
JS:
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
Child Component
Use this.$emit('event_name') to send an event to the parent component.
Parent Component
In order to listen to that event in the parent component, we do v-on:event_name and a method (ex. handleChange) that we want to execute on that event occurs
Done :)
I agree with the event emitting and v-model answers for those above. However, I thought I would post what I found about components with multiple form elements that want to emit back to their parent since this seems one of the first articles returned by google.
I know the question specifies a single input, but this seemed the closest match and might save people some time with similar vue components. Also, no one has mentioned the .sync modifier yet.
As far as I know, the v-model solution is only suited to one input returning to their parent. I took a bit of time looking for it but Vue (2.3.0) documentation does show how to sync multiple props sent into the component back to the parent (via emit of course).
It is appropriately called the .sync modifier.
Here is what the documentation says:
In some cases, we may need “two-way binding” for a prop.
Unfortunately, true two-way binding can create maintenance issues,
because child components can mutate the parent without the source of
that mutation being obvious in both the parent and the child.
That’s why instead, we recommend emitting events in the pattern of
update:myPropName. For example, in a hypothetical component with a
title prop, we could communicate the intent of assigning a new value
with:
this.$emit('update:title', newTitle)
Then the parent can listen to
that event and update a local data property, if it wants to. For
example:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
For convenience, we offer a shorthand for this pattern with the .sync modifier:
<text-document v-bind:title.sync="doc.title"></text-document>
You can also sync multiple at a time by sending through an object. Check out the documentation here
The way more simple is use this.$emit
Father.vue
<template>
<div>
<h1>{{ message }}</h1>
<child v-on:listenerChild="listenerChild"/>
</div>
</template>
<script>
import Child from "./Child";
export default {
name: "Father",
data() {
return {
message: "Where are you, my Child?"
};
},
components: {
Child
},
methods: {
listenerChild(reply) {
this.message = reply;
}
}
};
</script>
Child.vue
<template>
<div>
<button #click="replyDaddy">Reply Daddy</button>
</div>
</template>
<script>
export default {
name: "Child",
methods: {
replyDaddy() {
this.$emit("listenerChild", "I'm here my Daddy!");
}
}
};
</script>
My full example: https://codesandbox.io/s/update-parent-property-ufj4b
It is also possible to pass props as Object or Array. In this case data will be two-way binded:
(This is noted at the end of topic: https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow )
Vue.component('child', {
template: '#child',
props: {post: Object},
methods: {
updateValue: function () {
this.$emit('changed');
}
}
});
new Vue({
el: '#app',
data: {
post: {msg: 'hello'},
changed: false
},
methods: {
saveChanges() {
this.changed = true;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<p>Parent value: {{post.msg}}</p>
<p v-if="changed == true">Parent msg: Data been changed - received signal from child!</p>
<child :post="post" v-on:changed="saveChanges"></child>
</div>
<template id="child">
<input type="text" v-model="post.msg" v-on:input="updateValue()">
</template>
In Parent Conponent -->
data : function(){
return {
siteEntered : false,
};
},
In Child Component -->
this.$parent.$data.siteEntered = true;
2021 ANSWER - Vue 2.3+
SHORT ANSWER: Just add .sync modifier in the parent and pass the data as props to the children:
// PARENT:
data () {
return {
formData: {
members: [] //<- we wanna pass this one down to children and add/remove from the child component
}
}
// PARENT TEMPLATE:
<!-- ADD MEMBERS -->
<add-members :members.sync="formData.members" />
Nested child component: AddMembers.vue
export default {
name: 'AddMembers',
props: ['members'],
methods: {
addMember () {
this.members.push(new Member()) // <-- you can play and reactivity will work (in the parent)
},
removeMember (index) {
console.log('remove', index, this.members.length < 1)
this.members.splice(index, 1)
}
}
}
Long story: changes from the child component in reallity are being $emitted and updating formData.members[] of the parent.
source: Mauro Perez at medium
In the child
<input
type="number"
class="form-control"
id="phoneNumber"
placeholder
v-model="contact_number"
v-on:input="(event) => this.$emit('phoneNumber', event.target.value)"
/>
data(){
return {
contact_number : this.contact_number_props
}
},
props : ['contact_number_props']
In parent
<contact-component v-on:phoneNumber="eventPhoneNumber" :contact_number_props="contact_number"></contact-component>
methods : {
eventPhoneNumber (value) {
this.contact_number = value
}
The correct way is to $emit() an event in the child component that the main Vue instance listens for.
// Child.js
Vue.component('child', {
methods: {
notifyParent: function() {
this.$emit('my-event', 42);
}
}
});
// Parent.js
Vue.component('parent', {
template: '<child v-on:my-event="onEvent($event)"></child>',
methods: {
onEvent: function(ev) {
v; // 42
}
}
});
When we want to pass the data to the parent component as well as another nested child component of the current child component, using a data property would be useful as shown in the following example.
Example:
Calling your child component from the parent component like this.
Parent component:
<template>
<TodoItem :todoParent="todo" />
</template>
<script>
export default {
data() {
return {
todo: {
id:1,
task:'todo 1',
completed:false
}
};
}
}
</script>
Child component:
<template>
<div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{todo.task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todoParent"],
data() {
return {
todo: this.todoParent,
};
},
methods: {
markCompleted() {
this.todo.completed = true
},
},
};
</script>
Even you can pass this property to the nested child component and it won't give this error/warning.
Other use cases when you only need this property sync between parent and child component. It can be achieved using the sync modifier from Vue. v-model can also be useful. Many other examples are available in this question thread.
Example2: using component events.
We can emit the event from the child component as below.
Parent component:
<template>
<TodoItem :todo="todo" #markCompletedParent="markCompleted" />
</template>
<script>
export default {
data() {
return {
todo: {
id:1,
task:'todo 1',
completed:false
}
};
},
methods: {
markCompleted() {
this.todo.completed = true
},
}
}
</script>
Child component:
<template>
<div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{todo.task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todo"],
methods: {
markCompleted() {
this.$emit('markCompletedParent', true)
},
}
};
</script>
Another way is to pass a reference of your setter from the parent as a prop to the child component, similar to how they do it in React.
Say, you have a method updateValue on the parent to update the value, you could instantiate the child component like so: <child :updateValue="updateValue"></child>. Then on the child you will have a corresponding prop: props: {updateValue: Function}, and in the template call the method when the input changes: <input #input="updateValue($event.target.value)">.
I don't know why, but I just successfully updated parent data with using data as object, :set & computed
Parent.vue
<!-- check inventory status - component -->
<CheckInventory :inventory="inventory"></CheckInventory>
data() {
return {
inventory: {
status: null
},
}
},
Child.vue
<div :set="checkInventory">
props: ['inventory'],
computed: {
checkInventory() {
this.inventory.status = "Out of stock";
return this.inventory.status;
},
}
his example will tell you how to pass input value to parent on submit button.
First define eventBus as new Vue.
//main.js
import Vue from 'vue';
export const eventBus = new Vue();
Pass your input value via Emit.
//Sender Page
import { eventBus } from "../main";
methods: {
//passing data via eventbus
resetSegmentbtn: function(InputValue) {
eventBus.$emit("resetAllSegment", InputValue);
}
}
//Receiver Page
import { eventBus } from "../main";
created() {
eventBus.$on("resetAllSegment", data => {
console.log(data);//fetching data
});
}
I think this will do the trick:
#change="$emit(variable)"
Intro
I was looking for sending data from parent to child (and back) in vue3 (I know the question was about vue2, but there are no references for vue3 on SO at the time).
Below is the working boilerplate result, pure "html + js", no packagers, modules, etc with few caveats I had, explained.
Notes:
Tnserting the child - line
<component-a :foo="bar" #newfooevent="bar = $event"></component-a>`
I bind parent.bar to child.foo using short-hand :foo="bar", same as v-bind:foo="bar". It passes data from parent to child through props.
Caveat: Event listener should be placed in the child component tag only!
That is the #newfooevent="bar = $event" part.
You cannot catch the signal in the <div id="app"> or anywhere else inside the parent.
Still, this is the parent's side of the universe, and here you can access all parent's data and extract the data from the child's signal to deal with it.
You can create app, and define component after it (the app.component("component-a", ...) part.
Caveat: there are no need in forward declaration of components, e.g. functions in C/C++. You can create app which uses the component, and define the component afterwards. I lost a lot of time looking for the way to declare it somehow - no need.
Here you can find a nice example of the v-model usage, and the code I used to sort things out: https://javascript.plainenglish.io/vue-3-custom-events-d2f310fe34c9
The example
<!DOCTYPE html>
<html lang="en">
<head>
<title>App</title>
<meta charset="utf-8" />
<script src="https://unpkg.com/vue#next"></script>
</head>
<body>
<div id="app">
<component-a :foo="bar" #newfooevent="bar = $event"></component-a>
<p>Parent copy of `bar`: {{ bar }}</p>
<button #click="bar=''">Clear</button>
</div>
<script>
const app = Vue.createApp({
data() {
return {
bar: "bar start value"
};
}
});
app.component("component-a", {
props: {
foo: String
},
template: `
<input
type="text"
:value="foo"
#input="$emit('newfooevent', $event.target.value)">
`
});
app.mount("#app");
</script>
</body>
</html>
There is another way of communicating data change from child to parent which uses provide-inject method. Parent component "provides" data or method for the child component, and this data or method is then "injected" into child component - but it can also be used for triggering a method in parent and passing it a parameter.
This approach can be especially useful when having a child component which happens to be embedded in multiple other components. Also, in a large project care must be taken not to lose overview of provide and inject usage.
Example of parent (top level) component App.vue using provide to give access to it's method updateParentValue (if method is provided and not data, provide is in form of a method):
<template>
<h2>App.vue, parentValue is: <em>{{ parentValue }}</em></h2>
<ChildComponent1 />
</template>
<script>
import ChildComponent1 from "./components/ChildComponent1.vue";
export default {
data() {
return {
parentValue: "",
};
},
components: {
ChildComponent1,
},
provide() {
return {
updateParent: this.updateParentValue,
};
},
methods: {
updateParentValue($value) {
this.parentValue = $value;
},
},
};
</script>
In this example component Component4.vue is in the "bottom", that is, App.vue contains Component1, Component1 contains Component2... until Component4 which actually utilizes inject to get access to parent method which is then invoked and a parameter $value is passed (just a random number here):
<template>
<div>
<h2>ChildComponent4.vue</h2>
<button #click="updateParent(Math.random())">
Update parent value in App.vue
</button>
</div>
</template>
<script>
export default {
inject: ["updateParent"],
};
</script>
Entire example is available here.
Vue.js documentation

Categories

Resources