I'm working on VueJS2 with 2 components.
Desired behaviour: Each time a method is triggered in one component, i would like to trigger a method in the other component.
I assume watch and $refs are what I need. this is what it looks like :
watch: {
'this.$refs.component1.method1': () => {
console.log('TRIGGERED method1')
this.$refs.component2.method1()
},
'this.$refs.component1.method2': () => {
console.log('TRIGGERED metdod2')
this.$refs.component2.method2()
}
Unfortunately, this doesn't work. Is it possible to watch a method call?
Typically watchers and refs are not used in this scenario. What you can use depends a bit how the components are organized. If you want to watch a method from child to parent you can simply listen to a custom event in the component. That way you would emit the event from the component using $emit(customevent). You can then add the listener in the parent component using #customevent="yourMethod".
The vue docs explain that very nicely:
https://v2.vuejs.org/v2/guide/components-custom-events.html
When they do not have a parent child relationship the event bus is what you need. This typically means that you create a .js file called eventbus.js or something like that containing this:
import Vue from 'vue';
export const EventBus = new Vue();
You can then import your eventbus.js in every component where you want to exchange events and then emit events to the global evenbus like this:
import { EventBus } from './event-bus.js';
export default {
methods: {
EmitmyMethod () {
EventBus.$emit('customevent')
},
ListenToMyMethod () {
EventBus.$on('customevent')
}
}
}
More info about that here:
https://alligator.io/vuejs/global-event-bus/
Related
I have this following vuejs component hierarchy.
What i want to do it to invoke COMP_B_ONE validate() method, when COMP_A_TWO submit() method is invoked EVERY TIME.
MAIN_COMPONENT
COMP_A_ONE
COMP_B_ONE
validate()
COMP_B_TWO
validate()
COMP_A_TWO
submit()
I've already implemented an emit when submit is triggered in COMP_A_TWO which can be listened in MAIN_COMPONENT
submit() {
this.$emit('submit')
}
what seems to be the best approach in this regard? any suggestions appreciated.
I can get this done by two ways.
1 - Global EventBus
I will create an eventBus and register events on it from any file and listen it anywhere -
import { EventBus } from '#/eventBus'
// simply import it to component which need listen the event
//Register Event where you have your methods - like In your COMP_B_TWO
EventBus.$on('validate', () => { this.validate() })
// Emit event from another component
EventBus.$emit('validate')// Like directly from your COMP_A_TWO
To know how to create a eventBus follow this - Global Event Bus Vue
Another way I can think is
2 - Refs
Add reference to COMP_A_ONE like
<COMP_A_ONE ref = "one" />
Then add reference to COMP_B_ONE
<COMP_B_ONE ref = "b-one" />
Now when you trigger submit from main component
execute it -
this.$on('submit', () => {
this.$refs.one['b-one'].validate()
})
It totally depends which way you wanna go -
If you need to call validate for many more places, I would suggest choosing EventBus
You just need current component to have it, use Refs
I have a parent component set up with data property(boolean) like this:
<script>
import PmhnEntryAll from './components/PmhnEntryAll'
import {store} from './store.js'
export default {
name: 'PmhnApp',
components: {
PmhnEntryAll,
},
data: function () {
return {
vPmhnEntryAll: store.componentVisibility.vPmhnEntryAll,
}
},
}
</script>
and in child component I want to set the vPmhnEntryAll to 'true'. How do I go about this ?
this is usually done using v-on: in parent and $emit in child
check out https://v2.vuejs.org/v2/guide/components-custom-events.html for docs around events emitting
you can also go further and v-bind which is a form of event listening, but it's limited in what applications you can use it for. https://v2.vuejs.org/v2/guide/components-custom-events.html#Binding-Native-Events-to-Components
You can also pass a function to update a variable, but this is considered an anti-pattern and not recommended.
And depending on the structure of your project, you can uses busses or vuex for managing global scope.
I am using a date picker component library and i want to watch when a property of that component changes.
I have tried this:
watch: {
'$refs.picker.popupVisible': {
handler (new_value) {
console.log('executed')
},
deep: true
}
}
Also this:
computed: {
picker () {
console.log(this.$refs.picker.popupVisible)
return this.$refs.picker.popupVisible
}
}
I know that the solution will be a vue.js hack because this is not the right way.If i had access to child component i would emit en event to parent but unfortunately i don't have.
I had a similar problem using a library which had some limitations.
Unfortunately, your watcher will not work.You have to use the function watcher to make this to work.And you have to use it inside the mounted hook.
mounted() {
this.$watch(
"$refs.picker.popupVisible",
(new_value, old_value) => {
//execute your code here
}
);
}
I also have an example. Please take a look here
What you can do is create a data object in parent component and include the date field in that data object and pass that data object to child component as props
<child :dateObj="dateObj"></child>
data: {
dateObj: {
date: ""
}
}
And in child component you can use the date field of that dateObj props. This is possible because Vue doesn't watch the property of Objects passed as props and we can modify them without Vue complaining in console.
Thus the changed date field is reflected in parent as well.
as title, how can I do that
from offical documentation just tell us that $delete can use argument 'object' and 'key'
but I want delete a component by itself like this
this.$delete(this)
I couldn't find instructions on completely removing a Vue instance, so here's what I wound up with:
module.exports = {
...
methods: {
close () {
// destroy the vue listeners, etc
this.$destroy();
// remove the element from the DOM
this.$el.parentNode.removeChild(this.$el);
}
}
};
Vue 3 is basically the same, but you'd use root from the context argument:
export default {
setup(props, { root }){
const close = () => {
root.$destroy();
root.$el.parentNode.removeChild(root.$el);
};
return { close };
}
}
In both Vue 2 and Vue 3 you can use the instance you created:
const instance = new Vue({ ... });
...
instance.$destroy();
instance.$el.parentNode.removeChild(instance.$el);
No, you will not be able to delete a component directly. The parent component will have to use v-if to remove the child component from the DOM.
Ref: https://v2.vuejs.org/v2/api/#v-if
Quoted from docs:
Conditionally render the element based on the truthy-ness of the expression value. The element and its contained directives / components are destroyed and re-constructed during toggles.
If the child component is created as part of some data object on parent, you will have to send an event to parent via $emit, modify (or remove) the data and the child component will go away on its own. There was another question on this recently: Delete a Vue child component
You could use the beforeDestroy method on the component and make it remove itself from the DOM.
beforeDestroy () {
this.$root.$el.parentNode.removeChild(this.$root.$el)
},
If you just need to re-render the component entirely you could bind a changing key value to the component <MyComponent v-bind:key="some.changing.falue.from.a.viewmodel"/>
So as the key value changes Vue will destroy and re-render your component.
Taken from here
I'm using the vue-cli scaffold for webpack
My Vue component structure/heirarchy currently looks like the following:
App
PDF Template
Background
Dynamic Template Image
Static Template Image
Markdown
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:
Vuex and when to use it
Vuex alternative solution for smaller apps
In my child component, there are no buttons to emit changed data. It's a form with somewhat 5~10 inputs. the data will be submitted once you click the process button in another component. so, I can't emit every property when it's changing.
So, what I did,
In my parent component, I can access child's data from "ref"
e.g
<markdown ref="markdowndetails"></markdown>
<app-button #submit="process"></app-button>
// js
methods:{
process: function(){
// items is defined object inside data()
var markdowns = this.$refs.markdowndetails.items
}
}
Note: If you do this all over the application I suggest move to vuex instead.
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.
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)
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;
}
},
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.