Reputation: 3062
I'm using the vue-cli scaffold for webpack
My Vue component structure/heirarchy currently looks like the following:
At the app level, I want a vuejs component method that can aggregate all of the child component's data into a single JSON object that can be sent off to the server.
Is there a way to access child component's data? Specifically, multiple layers deep?
If not, what is the best practice for passing down oberservable data/parameters, so that when it's modified by child components I have access to the new values? I'm trying to avoid hard dependencies between components, so as of right now, the only thing passed using component attributes are initialization values.
UPDATE:
Solid answers. Resources I found helpful after reviewing both answers:
Upvotes: 107
Views: 166832
Reputation: 4510
In certain scenarios, the parent can directly access their child's data using its reference
e.g
<markdown ref="markdowndetails"></markdown>
<app-button @submit="process"></app-button>
// js
methods:{
process: function(){
// items is a defined object inside data()
var markdowns = this.$refs.markdowndetails.items
}
}
for complex state management scenarios, an effective approach would be to use Vuex.
Upvotes: 98
Reputation: 3487
Nobody's mentioned the use of Provide/Inject. I'm not saying this is the best way, but it is "a" way of creating a simple grand-parent/grand-child reactivity without having to worry about the complexities of data-stores.
As usual, the following solution comes with a health warning. Only use it on small-scale simplified apps where you know the grand-child will always be a descendant of the grandfather! Blah, blah....
(By the way, this works in Vue3 with Composition API.)
// Grandparent component
<script setup>
import { ref, provide } from 'vue'
import Child from './Child.vue'
const message = ref('hello')
function changeMessage(e) {
message.value = e.target.value;
}
provide('message', { message, changeMessage});
</script>
<template>
<p>The value from the grandchild is: {{ message }}</p>
<Child />
</template>
In my simple example, the Child component does nothing but to prove that the reactivity logic can pass it by:
// file: Child.vue
<script setup>
import GrandChild from './GrandChild.vue'
</script>
<template>
<GrandChild />
</template>
And the Grand-child looks like this:
// file: GrandChild.vue
<script setup>
import { inject } from 'vue'
const { message, changeMessage } = inject('message');
</script>
<template>
<p>
Message to send grandparent: <input :value="message" @input="changeMessage" />
</p>
</template>
When you type your message in the <input />
box on the grand-child, the grand-parent will reflect that message.
Documentation: https://vuejs.org/guide/components/provide-inject.html
Upvotes: 1
Reputation: 393
In my case I have a registration form that I've broken down into components.
As suggested above I used $refs, In my parent I have for example:
In Template:
<Personal ref="personal" />
Script - Parent Component
export default {
components: {
Personal,
Employment
},
data() {
return {
personal: null,
education: null
}
},
mounted: function(){
this.personal = this.$refs.personal.model
this.education = this.$refs.education.model
}
}
This works well as the data is reactive.
Upvotes: 5
Reputation: 221
you can meke ref to child component and use it as this this.$refs.refComponentName.$data
parent-component
<template>
<section>
<childComponent ref="nameOfRef" />
</section>
</template>
methods: {
save() {
let Data = this.$refs.nameOfRef.$data;
}
},
Upvotes: 17
Reputation: 15924
what is the best practice for passing down oberservable data/parameters, so that when it's modified by child components I have access to the new values?
The flow of props is one way down, a child should never modify its props directly.
For a complex application, vuex is the solution, but for a simple case vuex is an overkill. Just like what @Belmin said, you can even use a plain JavaScript object for that, thanks to the reactivity system.
Another solution is using events. Vue has already implemented the EventEmitter interface, a child can use this.$emit('eventName', data)
to communicate with its parent.
The parent will listen on the event like this: (@update
is the shorthand of v-on:update
)
<child :value="value" @update="onChildUpdate" />
and update the data in the event handler:
methods: {
onChildUpdate (newValue) {
this.value = newValue
}
}
Here is a simple example of custom events in Vue:
http://codepen.io/CodinCat/pen/ZBELjm?editors=1010
This is just parent-child communication, if a component needs to talk to its siblings, then you will need a global event bus, in Vue.js, you can just use an empty Vue instance:
const bus = new Vue()
// In component A
bus.$on('somethingUpdated', data => { ... })
// In component B
bus.$emit('somethingUpdated', newData)
Upvotes: 35
Reputation: 9201
For this kind of structure It's good to have some kind of Store.
VueJS provide solution for that, and It's called Vuex.If you are not ready to go with Vuex, you can create your own simple store.
Let's try with this
MarkdownStore.js
export default {
data: {
items: []
},
// Methods that you need, for e.g fetching data from server etc.
fetchData() {
// fetch logic
}
}
And now you can use those data everywhere, with importing this Store file
HomeView.vue
import MarkdownStore from '../stores/MarkdownStore'
export default {
data() {
sharedItems: MarkdownStore.data
},
created() {
MarkdownStore.fetchData()
}
}
So that's the basic flow that you could use, If you dont' want to go with Vuex.
Upvotes: 60