Fullcalendar - Prevent Event Overlap When dateClick Event Fired - vue.js

I would like to prevent a new slot being created when already occupied. For example, if the dates selected overlap e.g. Slot one has 10am to 11am and potential added slot is 10:15 to 11:15, I want to block this as some of the time is already taken.
Currently, I am trying to use the dateEvent option in Vue but it still creates the slot. I wanted to get the current slot data but can't find a way of doing this and eventOverlap and slotEventOverlap seem to do nothing in this context.
Has anyone run into this problem before?
Code Example:
export default {
components: {
FullCalendar
},
mounted() {
console.log('Component mounted.')
},
data() {
return {
allEvents: [],
calendarOptions: {
plugins: [timeGridPlugin, dayGridPlugin, interactionPlugin],
initialView: 'timeGridDay',
selectable: true,
selectMirror: true,
allDaySlot: false,
slotDuration: "00:15:00",
slotMinTime: '09:00:00',
slotMaxTime: '19:00:00',
editable: false,
eventLimit: true,
eventOverlap: false,
slotEventOverlap: false,
dateClick: this.handleDateClick,
events: function (fetchInfo, successCallback, failureCallback) {
//...
},
}
}
},
methods: {
handleDateClick: function (arg) {
console.log(arg, arg.view.getCurrentData())
let startDateTime = new Date(arg.dateStr)
let endDateTime = new Date(arg.dateStr)
const calendar = this.$refs.fullCalendar.getApi()
var event = calendar.getEventById('my-event')
if (event) {
event.remove()
}
calendar.addEvent({
id: 'my-event',
title: 'Selected Slot',
start: startDateTime,
end: new Date(endDateTime.setHours(endDateTime.getHours() + 1))
})
calendar.unselect()
}
},
}

Related

Loop over method in vue

I want to be able to loop over all the invoices for a given client-year-month
I use django_filter in DRF to do some filtering on the backend, my endpoints look like this:
all invoices: http://127.0.0.1:8000/invoices/
filters: http://127.0.0.1:8000/invoices/?client=2&year=2020&month=5
I have a method that gets the results based on the filter, and a watch property for retrieving the data at the right time, in this case when a month is chosen.
methods: {
retrieveResults() {
this.$axios.$get('/invoices/', {
params: {
client: this.client,
month: this.month,
year: this.year,
},
});
},
},
watch: {
month: {
handler: 'retrieveResults',
},
},
The response I get looks like this (simplified):
[{
"id":119,
"client":2,
"invoice_id":"2020001",
"order_date":"2020-05-07",
},
{
"id":120,
"client":2,
"invoice_id":"2020002",
"order_date":"2020-05-07",
}]
Everything is working as expected, I see the right results in my network tab depending on the choices, my question is how do I v-for loop over this? I've tried numerous things, nothing has worked so far.
I tried wrapping the retrieveResults in a vuetify v-data-table, without success.
Something like this:
// in a template:
<div v-for="invoice in invoices" :key="invoice.id">
// here is a component(s) for showing invoice content
</div>
...
// in a component:
data: {
return {
invoices: []
}
},
methods: {
async retrieveResults() {
const { data: invoices} = await this.$axios.$get('/invoices/', {
params: {
client: this.client,
month: this.month,
year: this.year,
},
});
this.invoices = invoices
}
}

Set data field from getter and add extra computed field

I wanted to set fields inside data using getters:
export default {
data () {
return {
medications: [],
}
},
computed: {
...mapGetters([
'allMedications',
'getResidentsById',
]),
I wanted to set medications = allMedications, I know that we can user {{allMedications}} but my problem is suppose I have :
medications {
name: '',
resident: '', this contains id
.......
}
Now I wanted to call getResidentsById and set an extra field on medications as :
medications {
name: '',
resident: '', this contains id
residentName:'' add an extra computed field
.......
}
I have done this way :
watch: {
allMedications() {
// this.medications = this.allMedications
const medicationArray = this.allMedications
this.medications = medicationArray.map(medication =>
({
...medication,
residentName: this.getResidentName(medication.resident)
})
);
},
},
method: {
getResidentName(id) {
const resident = this.getResidentsById(id)
return resident && resident.fullName
},
}
But this seems problem because only when there is change in the allMedications then method on watch gets active and residentName is set.
In situations like this you'll want the watcher to be run as soon as the component is created. You could move the logic within a method, and then call it from both the watcher and the created hook, but there is a simpler way.
You can use the long-hand version of the watcher in order to pass the immediate: true option. That will make it run instantly as soon as the computed property is resolved.
watch: {
allMedications: {
handler: function (val) {
this.medications = val.map(medication => ({
...medication,
residentName: this.getResidentName(medication.resident)
});
},
immediate: true
}
}

vuejs add/remove class by referencing bool in an array

I am adding/removing a class based on boolean value. Works as just a bool but not if bool in array
This line works, the opaque class gets added/removed:
<img v-bind:class="{opaque: slide1}" src="img/forest-field.jpg" />
This line does not work, the opaque class does not get added/removed:
<img v-bind:class="{opaque: slideBools[0]}" src="img/forest-field.jpg" />
the code in instance data:
new Vue({
el: '#myCarousel',
data: {
slideBools: [true, false, false, false],
slide1: true,
slide2: false,
slide3: false,
slide4: false,
},
},
});
I am updating the bool value in an instance method like this:
methods: {
startSlides: function () {
var vm = this;
setInterval(() => {
vm.slideBools[vm.curImage] = true;
}, 4000);
},
}
Update question: I am wondering if how I reference the array value from html is where the problem lies
While updating the array value use Vue.set or its alias this.$set method to reflect in the DOM since its a nested property updating directly won't reflect in all cases.
this.$set(this.slideBools, 0, true)
Refer Vue documentation about reactivity for more information.
UPDATE : In your case, the code would be like as follows:
methods: {
startSlides: function () {
var vm = this;
setInterval(() => {
v.$set(vm.slideBools, vm.curImage, true);
}, 4000);
},
}
Try changing your data prop to:
data() {
return {
slideBools: [true, false, false, false],
slide1: true,
slide2: false,
slide3: false,
slide4: false,
};
},

Getting documents with ID from firstore collection

While using Firestore, vuefire, vue-tables-2, I stuck getting document's id.
My data structure is as below.
Here is my code.
<v-client-table :columns="columns" :data="devices" :options="options" :theme="theme" id="dataTable">
import { ClientTable, Event } from 'vue-tables-2'
import { firebase, db } from '../../firebase-configured'
export default {
name: 'Devices',
components: {
ClientTable,
Event
},
data: function() {
return {
devices: [],
columns: ['model', 'id', 'scanTime', 'isStolen'],
options: {
headings: {
model: 'Model',
id: 'Serial No',
scanTime: 'Scan Time',
isStolen: 'Stolen YN'
},
templates: {
id: function(h, row, index) {
return index + ':' + row.id // <<- row.id is undefined
},
isStolen: (h, row, index) => {
return row.isStolen ? 'Y': ''
}
},
pagination: {
chunk: 5,
edge: false,
nav: 'scroll'
}
},
useVuex: false,
theme: 'bootstrap4',
template: 'default'
}
},
firestore: {
devices: db.collection('devices')
},
};
My expectation is devices should id property as vuefire docs.
But array this.devices didn't have id field even if I check it exist it console.
Basically, every document already has id attribute, but it's non-enumerable
Any document bound by Vuexfire will retain it's id in the database as
a non-enumerable, read-only property. This makes it easier to write
changes and allows you to only copy the data using the spread operator
or Object.assign.
You can access id directly using device.id. But when passing to vue-tables-2、devices is copied and lost id non-enumerable attribute.
I think you can workaround using computed property
computed: {
devicesWithId() {
if (!this.devices) {
return []
}
return this.devices.map(device => {
...device,
id: device.id
})
}
}
Then, please try using devicesWithId in vue-tables-2 instead.

FullCalendar 4.0 within a Vue application

I am using fullCalendar v4.0 with no jquery. I have initialized it like this
<div id="calendar"></div>
In data object I have this.
calendar: null,
config: {
plugins: [ interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin, momentPlugin],
axisFormat: 'HH',
defaultView: 'timeGridWeek',
allDaySlot: false,
slotDuration: '00:60:00',
columnFormat: 'dddd',
titleFormat: 'dddd, MMMM D, YYYY',
defaultDate: '1970-01-01',
dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
eventLimit: true,
eventOverlap: false,
eventColor: '#458CC7',
firstDay: 1,
height: 'auto',
selectHelper: true,
selectable: true,
timezone: 'local',
header: {
left: '',
center: '',
right: '',
},
select: (event) => {
this.selectCalendar(event)
},
header: false,
events: null
}
}
while having a calendar in data variable, Now I can render() and destroy() it from anywhere.
But I am having an issue for handling Calendar events:
Such as
select: (event) => {
this.selectCalendar(event)
}
I have defined another method in methods: {} as selectCalendar() to call it in select but I am getting an error as
Uncaught TypeError: Cannot read property 'selectCalendar' of undefined
I want to do few operations on select, eventClick, eventDrop, eventResize, but I am unable to call a method within the config.
Also is there any way possible to define select or any method as
select: this.selectCalendar
So that it will just straight send an event to the defined method?
I have tried vue-fullcalendar but it doesn't work for my cause. Any help will be thankful.
Vue v.2.5.21
I am using vue full calendar, you can handle event of fullcalendar like code below
<full-calendar :event-sources="eventSources" #event-selected="myEventSelected"></full-calendar>
export default{
methods:{
caculateSomething(event){
//do st here
},
myEventSelected(event){
//do st here
this.caculateSomething(event)
console.log(event)
}
}
}
This is how I sorted this out.
in html <div id="calendar"></div>
in your data() => {}
calendar: null,
config: {
plugins: [ interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin, momentPlugin],
axisFormat: 'HH',
defaultView: 'timeGridWeek',
allDaySlot: false,
slotDuration: '00:60:00',
columnFormat: 'dddd',
columnHeaderFormat: { weekday: 'short' },
defaultDate: '1970-01-01',
dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
eventLimit: true,
eventOverlap: false,
eventColor: '#458CC7',
firstDay: 1,
height: 'auto',
selectHelper: true,
selectable: true,
timezone: 'UTC',
header: {
left: '',
center: '',
right: '',
},
header: false,
editable: true,
events: null
}
Don't define any select, resize or dropEvent in config for the first time, but then the part where you are going to render the calendar do something like this
if (this.calendar == null) {
console.log(this.schedule)
let calendarEl = document.getElementById('calendar');
let calendarConfig = this.config
let select = (event) => {
this.selectCalendar(event)
}
let eventClick = (event) => {
this.clickCalendar(event)
}
let eventDrop = (event) => {
this.dropCalendar(event)
}
let eventResize = (event) => {
this.resizeCalendar(event)
}
calendarConfig.select = select;
calendarConfig.eventClick = eventClick;
calendarConfig.eventDrop = eventDrop;
calendarConfig.eventResize = eventResize;
this.calendar = new Calendar(calendarEl, calendarConfig);
this.calendar.render();
this.renderEvents()
}
Now you can handle those events of FullCalendar in your own methods.
Also having the calendar in this.calendar gives you the power to destroy it from anywhere, in the methods: {}
In FullCalendar 4.0 things have been changes but quite simpler.
these are the methods attached to FullCalendar Events
selectCalendar(event) {
this.calendar.addEvent(event)
},
clickCalendar(event) {
if (window.confirm("Are you sure you want to delete this event?")) {
let findingID = null
if (event.event.id === "") {
findingID = event.el
} else {
findingID = event.event.id
}
let removeEvent = this.calendar.getEventById( findingID )
removeEvent.remove()
}
},
dropCalendar(event) {
},
resizeCalendar(event) {
},
destroyCalendar() {
if (this.calendar != null) {
this.calendar.destroy();
this.calendar = null
}
}
when an event is added by you. You can find it through el in an event, but the custom events should have a unique ID. through which you will find and delete it.