To sumarize what I'm trying to achieve, I'm creating a calendar, where each day is a component instance, and inside each day there can be zero to multiple instances of a smaller component, named Task, kind of like this.
<Root>
<CalendarDay>
<Task></Task>
<Task></Task>
...
</CalendarDay>
<CalendarDay></CalendarDay>
...
</Root>
The number of smaller components to draw is dependent of a data array placed on the root component, which is expected to change multiple times via API calls.
The flow I had in mind is, first draw the CalendarDay components, then make an API call to obtain the Tasks that must be drawn on all days drawn, and finally fire a global event so that each CalendarDay retrieves the Tasks assigned to it. To achieve it, I added an eventBus to Vue following the steps of an answer of another question here, which I can't find, but basically is setting an extra property on the Vue prototype.
Vue.prototype.$eventHub = new Vue(); // Global event bus
Everything is set up, and on the first execution the API retrieves the Tasks, a global event is fired, and all CalendarDay components retrieve and draw the Tasks; but when a second global event is fired, the listeners either don't receive anything or they are not responding.
Here are the root and components
// ROOT Component
var app = new Vue({
el: '#appVue',
data: function() {
return {
...
tasks: [],
calendar_days: [] // Each element in this array is an object with 2 properties: numday and weekday
...
}
},
methods: {
db_get_tasks_month: function(){
REMOTE_WS.get(`<API route>`)
.then(response => {
this.tasks= response.data.tasks;
this.$eventHub.$emit('sort-tasks-in-days');
})
.catch(err => {
console.error('err');
});
},
...
}
})
// CalendarDay Component
Vue.component('calendar-day', {
props: {'day': Object}, //{numday, weekday}
data: function(){
return {
tasks: []
};
},
methods: {
sortTasksInDays: function(){
this.tasks= this.$root.$data.tasks.filter(t => {
return t.hasOwnProperty("data_ts") && new Date(t.data_ts).getDate() === this.day.numday;
});
}
},
created: function(){ this.$eventHub.$on('sort-tasks-in-days', this.sortTasksInDays) },
beforeDestroy: function(){ this.$eventHub.$off('sort-tasks-in-days'); }
});
Could someone point out what am I doing wrong?
Thanks,
EDIT: To add a bit more info, I checked if the global event is even firing, and using the Vue plugin on Chrome I can confirm that it is indeed firing, so what I'm missing must be on the listeners. EDIT 2: Adding the property in ROOT where the calendar days are stored to display on a v-for
SOLVED: as Niels pointed out, passing the Tasks array as a prop was a solution. For future reference, the changes made to make it work are as follows:
// ROOT Component: The calendar_days data in root is this
data: function() {
return {
...
tasks: [],
calendar_days: [] // Each element in this array is an object with 3 properties: numday, weekday and tasks
...
}
},
methods: {
// The method db_get_tasks_month no longer does the global event emit
// The tasks are passed as a prop to each component
}
// CalendarDay Component
Vue.component('calendar-day', {
props: {
numday: { type: Number, default: 0},
weekday: { type: Number, default: 0},
tasks: { type: Array, default: []}
}
});
CodePudding user response:
First of all, I was wondering why you are using a global event bus for this. This issue seems rather trivial and can be solved using component properties. This would create a more readable structure.
As it appears, every calendar day needs an array of Tasks. This can be defined in the 'props'-property of your CalendarDay. Your CalendarDay holds too much inforamtion now. It is using root-information whch is later on filtered out. There's plenty of ways to get the data in the format you need but I think mapping whenever you fetch the data is the easiest. You map the response.data to the format:
days: {
'1': [],
'2': ['task A', 'Task B'],
...
}
Then it is a matter of looping over this object in your root-template and passing the tasks along as props (these are reactive).
<calendar-day v-for="(tasks, day) in days" :key="day" :tasks="tasks" :day-number="day"></calendar-day>