Vue JS Components structure - vue.js

I am learning Vue and my doubt is about the structure of my Vue app.
I learnt that the components can include both logic and template. Then I separated my components and everyone is getting the config from the main app (config is an object with the coordinates config.ll, config.lng).
I do the ajax call to my search-and-discovery API service and I display the results inside each components (current location, venues-near-you etc).
My question is: is it correct to encapsulate the calls into each components? Or is it better to get the needed data inside the general app and then share the results with the components using pros?
I am asking that because the hard part is starting now when I want to communicate the click of a category to the venuesNearYou component, I tried to use the emit without success.
//MAIN
<sidebar :config="config"></sidebar>
<content :config="config"></content>
//IN SIDEBAR
<currentLocation :config="config"></currentLocation>
<categories :config="config"></categories>
//IN CONTENT
<venueDetails :config="config"></venueDetails>
<venuesNearYou :config="config"></venuesNearYou>

I think you could use event Bus like approach
we have three type of communication in vue app (without vuex)
Parent to child communication which is full field by props
child to parent communication handle by custom event from child which is listen by parent
communication between non parent child component in which we use event bus approach
Parent to child example
Child to parent example
In child this.$emit('sendDataToParent',{someData:"some data"}})
in parent
<child-component :somedata="dataToChild" #sendDataToParent="'gotsomedata from parent'">
Event Bus
in main vue instance
const eventBus = new Vue()
in some component from where to send data
import eventBus
eventBus.$emit('someEvent','some data')
in some component from where to receive data
created() {
// register listener
eventBus.$on('someEvent',()=>{
})
}
For more reference
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props
https://v2.vuejs.org/v2/guide/components.html#Emitting-a-Value-With-an-Event
https://medium.com/easyread/vue-as-event-bus-life-is-happier-7a04fe5231e1

It's hard to help you around emitting an event since you didn't provide much of a code. But check Vuex. It serves as a centralized store for all the components in Vue application.

Related

Emit data between components (Event listener in vue?)

I'm kind of new to Vue and I'm trying to emit data between different components.
I have 2 components I need to emit their data together:
First component is the Item Selector.
Second component is Filter Navigation Bar.
the logic:
The navigation bar contains a filter section, I've created an ItemSelector component and import it to the navigation bar.
I want the data selected in the item selector will be emited to the container and a table will be draw by that information.
I created the logic of the table, and tried to import the item selector component directly to the container and passing the data using emit, and it worked great.
The problem is I've added one more page to the cycle.
Right now, the ItemSelector component emits its data to the FilterContainer component, and now this data should be emited to the container!
Is there any way to catch all those events in a seperate component, compute the data and emit it all over the app?
You can use a global event bus which can listen/emit event on global scope of your app.
Another approach is using vuex which store all global states of your app.
If you have small app, i think you should use event bus because it simpler than vuex.

Passing events from nested children(s) to parent in Vue

Im working on a Vue component that will be re-used in different projects (and will be available via npm install ...) and have principal question how the event emitting is handled.
The component itself contains few nested children elements like:
component-parent
|-child 1
|-child 2
|-child 3
|-child 4
When the component is included in some project then component-parent will expose all events that are emitted from the whole component. And was wandering how the in-component events are passed to the component-parent. For example if i want to pass event from child 4 to component-parent then child 4 emits to child 3 which emits to child 2 which finally emits to component-parent.
Such workflow is working fine but it doesn't look very practical.
Ive found out that EventBus can be used and seems more elegant solution but also seems that its not very popular. Is EventBus can be used freely or its considered to be a bit hacky/workaround way?
And another question regarding using vuex. If vuex is included in the component itself is it possible to emit from it to the project component (that have my component included)? Including vuex in my component will lead the main project to have two vuex instances. Is there are blockers for having multiple stores like this?
After some testing I've decided to go with EventBus. EventBus allows sending/receiving events between components. The components can be part of the same parent tree or on a complete different one. More information here
But in general event-bus.js file should be created
import Vue from 'vue';
export const EventBus = new Vue();
And then imported in any component that will use the EventBus mechanism
<script>
// Import the EventBus we just created.
import { EventBus } from './event-bus.js';
...
Then from the sending component emit the event
EventBus.$emit('i-got-clicked', this.clickCount);
Any component that have loaded EventBus can listen for events like this:
EventBus.$on('i-got-clicked', clickCount => {
console.log(`Oh, that's nice. It's gotten ${clickCount} clicks! :)`)
});
To stop listen for events:
EventBus.$off('i-got-clicked');
Personally i find EventBus quite easy and useful but I'll probably use them only when developing components that will be included in another projects. For the same project components Vuex is more than enough

How to set data value of other component in vue js?

I have two component for example Login.vue and Header.vue. I want to set data value of header.vue after some action in login.vue. how can i do so?I am using vue cli.
If your Login Component is a child of your header, then your Login can emit an event. See here.
If not, then your components can exchange data via a Global Event Bus
or a global store Vuex.
Also take a look here on cross component communication.

Invoke method in child component when the component is changed dynamically from the parent component

I have a simple component hierarchy, in which there is one parent component, which contains the following template <div><component :is="current"></component></div> and two child components, which are dynamically 'switched' out, via the value of current.
The logic for the 'switching' of components is being handled in the parent component. However, when I try to execute a method in the second child component (in response to an event emitted from the first child component, being listened to in the parent component, and altering the value of current in the parent component) through the mounted lifecycle hook, the expected result is not observed.
Essentially, I cannot find a decent way of invoking a child component's methods when that component is 'switched' in as the current component in the parent component.
I have realistically looked at using the $refs and $childrenproperties on the instance, but these do not seem to be of any assistance. I have also reasoned using the event system. I usually 'define' an event listener in the mounted lifecycle hook, however, I refer to the 'issue' of using the lifecycle hooks
What is the usual approach to this situation?
There are two options that immediately come to mind for child components invoking methods on being the "current" child component:
1) Using the mounted lifecycle hook. In order for this to work, you must be using v-if in order to conditionally display the child component, otherwise the child component will not trigger the mounted lifecycle hook. Otherwise, if you're using v-show or some other mechanism for conditionally displaying the child component, the mounted hook will only ever trigger once.
2) Using watched properties. In lieu of the above, you could do something like the following:
<child-component :target_prop="current"></child-component>
Vue.component('child-component', {
. . .,
props: ['target_prop'],
watch: {
target_prop: function() {
if(this.target_prop == your_expected_value) {
//execute the desired method for this component
}
}
}
});
This is, of course, just a proof-of-concept example and there are plenty of alternative ways to use the watched properties to your advantage.
I've also heard of using a "bus" to handle inter-component communication, but I don't believe it would be any better than the above two solutions (and is arguably worse).
Personally, I prefer the mounted lifecycle hook approach, though there's the caveat that any "settings" or data in your child components will be lost on switching between them (although you could also emit a settings object on destruction and store/restore those settings as needed, but that could get messy). You'll have to make the final judgment on which approach better suits your needs and which consequences you're more comfortable dealing with.

vuejs2 - how to create event buses for single file component hierarchies

I've found a solution by LinusBorg here, that registers a bus globally in any Vue instance. Is there a way to define this in a component hierarchy instead, so that I can create multiple scoped busses? Basically, if I had multiple "root"-level components with some children, an event bus should be instanciated for the "root"-level component and it's children, rather than all Vue instances.
I can't use the simple $emit and $on, because the hierarchy isn't restricted to plain parent-child communication. So events have to be passed over multiple levels.
You can create a js file like eventBus.js, and just export a vue instance:
import Vue from 'vue'
const bus = new Vue()
export default bus
then you can import the event bus in your .vue file
import bus from 'path/to/eventBus'
...
bus.$on('foo', ...)
Update my answer from the discussion in comments:
Since a event name is just a string, you can add a prefix/namespace to the event, like bus.$emit('domain.foo') or bus.$emit('domain/foo').
If you feel your application becomes more and more complex, just go for vuex.