Search code examples
javascriptvue.jsfullcalendarfullcalendar-4

How can I bind an array object to FullCalendar events?


Vue.component('full-calendar', {
    template: '<div ref="calendar"></div>',
    props: {
      tasks: {
        type: Array, 
        required: true
      },

      newtask: Object
    },
    watch: {
        tasks: function (task) { 
            
            // this.tasks.push(task);
            // this.cal.calendar.addEvent({
            //     title: task.title,
            //     start: task.start,
            //     end: task.end
            //  });
            

             $("#addTaskModal").modal('hide');

    }},
    data () {
      return {
        cal: null
      }
    },

    methods: {

        init:function(){

            var self = this;
        self.cal = $(self.$refs.calendar);
    
      var args = {
        firstDay: 1,
        lang: 'en',
        header: {
            left: 'prev,next today',
            center: 'title',
            right: 'addTaskButton / dayGridMonth,timeGridWeek,listMonth'
        },
        height: "auto",
        allDaySlot: false,
        slotEventOverlap: false,
        timeFormat: 'HH:mm',
        plugins: ["interaction", "dayGrid"],
        eventLimit: true,
        events: this.tasks,
        
        dateClick: function(date)
          {
              self.$emit('date_clicked', date);
              console.log('date clicked');
              console.log(this);
        },
  
        eventClick: function(event)
          {
                  self.$emit('event_clicked', event);
          },

          customButtons: {
            addTaskButton: {
                text: 'Add Task',
                click: function() {
                    $("#addTaskModal").modal('show');
                }
            }
        }
      }
      
      if (self.editable)
      {
        args.editable = true;
        args.eventResize = function(event)
        {
          self.$emit('event::resized', event);
        }
        
        args.eventDrop = function(event)
        {
          self.$emit('event::dropped', event);
        }
      }
      
      if (self.droppable)
      {
        args.droppable = true;
        args.eventReceive = function(event)
        {
          self.$emit('event::received', event);
        }
      }
      
      self.cal.calendar = new FullCalendar.Calendar(self.$el,args);

      self.cal.calendar.render();
        }


    },
    mounted () {

        this.init(); 
    }
    
  })
  
  let vm = new Vue({
    el: '#app',

    data () {
       return {
          tasks: [
             {
                title: 'Event1',
                start: '2019-05-10 12:30:00',
                end: '2019-05-10 16:30:00'
             },
             {
                title: 'Event2',
                start: '2019-05-07 17:30:00',
                end: '2019-05-07 21:30:00'
             }
          ],
          editable: false,
          selectedDate : moment().format('DD-MM-YYYY'),
          task: {
            title:null,
            start: '2019-05-07 17:30:00',
            end: '2019-05-07 21:30:00'
          },
          newtask: {}
       }
    },
    

    methods: {

        addTask: function(event){
            
            var d = moment().format('YYYY-MM-DD');

            var sel = new Date(this.selectedDate.replace( /(\d{2})-(\d{2})-(\d{4})/, "$2/$1/$3"));

            //console.log();
            
            var date = new Date(moment(sel).format('YYYY-MM-DD')+ 'T00:00:00'); // will be in local time
            
            this.tasks.push({
                title: this.task.title,
                start: date,
                end: date
              });

              this.$nextTick(function () {
                // DOM is now updated
                // `this` is bound to the current instance
                //this.doSomethingElse()
              })
        },

        dateClicked: function(date) {
            this.selectedDate = moment(date.date).format('DD-MM-YYYY');
          },

        eventClicked: function(event) {
            this.selectedDate = moment(event.event.start).format('DD-MM-YYYY');
          },

          showObject: function() {

            console.log(this.tasks);
          }
    
    
    }
  })
<link href="https://fullcalendar.io/releases/core/4.1.0/main.min.css" rel="stylesheet"/>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>



<script src="https://fullcalendar.io/releases/core/4.1.0/main.min.js"></script>
<script src="https://fullcalendar.io/releases/daygrid/4.1.0/main.min.js"></script>
<script src="https://fullcalendar.io/releases/interaction/4.1.0/main.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>



<div class="container mt-5" id="app">
    <div>

        <div class="row">
            <div class="col-md-8">
                <full-calendar 
                v-bind:tasks="tasks" 
                v-on:date_clicked="dateClicked"
                v-on:event_clicked="eventClicked"
                :newtask="newtask"
                ></full-calendar>
            </div>
            
        <div class="col-md-4">
          
            <div class="pb-3">
                    <span id="CurrentDate" class="" style="cursor: pointer;">{{selectedDate}}</span>
                    <span class="badge badge-primary">{{tasks.length}}</span>
                    <button @click="showObject">show object</button>
            </div>

          <table class="table table-condensed">
            <tbody v-for="task in tasks">
              <tr>
                <td>
                  <input
                    type="checkbox"
                  />
                </td>
                <td>{{task.title}}</td>
                <td></td>
              </tr>
            </tbody>
            <tbody>
              <tr v-if="tasks.length==0">
                <td colspan="3" class="text-center">No tasks to display</td>
              </tr>
            </tbody>
          </table>



 
        </div>
        </div>
    </div>

    <div class="modal fade" id="addTaskModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
        <div class="modal-dialog" role="document">
          <div class="modal-content">
            <div class="modal-header">
              <h5 class="modal-title" id="exampleModalLabel">Add a task</h5>
              <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                <span aria-hidden="true">&times;</span>
              </button>
            </div>
            <div class="modal-body">
                    <div class="form-group">
                            <input type="text" id="NewTaskTitle" placeholder="Enter task name" class="form-control" v-model="task.title" />
                    </div>
            </div>
            <div class="modal-footer">
              <button class="btn btn-success btn-sm" @click="addTask">Add</button>
              <button data-bind="click: CancelAddNewTask" class="btn btn-seconday btn-sm" data-dismiss="modal">Cancel</button>
            </div>
          </div>
        </div>
    </div>
</div>

Hi everyone,

I am trying to bind an object ‘tasks’ with full calendar events however it is not working.

Please see example above provided. View it in full page please

When you add a task, I am expecting it to be added to the tasks objects and of course to be be binded to the events in full calendar.

Basically, I want to bind tasks array to full calendar events object, so if for example I remove an item should also be removed from the full calendar events

Please let me know if I am doing it wrong . thanks a bunch

[1]:


Solution

  • It should be as simple as updating the FullCalendar when new tasks are sent from the Parent (new Vue) to Child (full-calendar).

    In your full-calendar component you should be able to just call the render() method which according to the FullCalendar docs will rerender the calendar if it already exists. In your architecture it is as simple as recalling the init() method. I also moved close modal logic to the appropriate place. This could probably user a bit more refactoriing but this should get you unstuck. Hope it helps.

    watch: {
      tasks: function (task) { 
        this.cal.calendar.render();
      }
    }
    

    https://fullcalendar.io/docs/render

    Vue.component('full-calendar', {
        template: '<div ref="calendar"></div>',
        props: {
          tasks: {
            type: Array, 
            required: true
          },
    
          newtask: Object
        },
        watch: {
          tasks: function (task) { 
                this.init()
          }
        },
        data () {
          return {
            cal: null
          }
        },
    
        methods: {
    
            init:function(){
    
                var self = this;
            self.cal = $(self.$refs.calendar);
        
          var args = {
            firstDay: 1,
            lang: 'en',
            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'addTaskButton / dayGridMonth,timeGridWeek,listMonth'
            },
            height: "auto",
            allDaySlot: false,
            slotEventOverlap: false,
            timeFormat: 'HH:mm',
            plugins: ["interaction", "dayGrid"],
            eventLimit: true,
            events: this.tasks,
            
            dateClick: function(date)
              {
                  self.$emit('date_clicked', date);
                  console.log('date clicked');
                  console.log(this);
            },
      
            eventClick: function(event)
              {
                      self.$emit('event_clicked', event);
              },
    
              customButtons: {
                addTaskButton: {
                    text: 'Add Task',
                    click: function() {
                        $("#addTaskModal").modal('show');
                    }
                }
            }
          }
          
          if (self.editable)
          {
            args.editable = true;
            args.eventResize = function(event)
            {
              self.$emit('event::resized', event);
            }
            
            args.eventDrop = function(event)
            {
              self.$emit('event::dropped', event);
            }
          }
          
          if (self.droppable)
          {
            args.droppable = true;
            args.eventReceive = function(event)
            {
              self.$emit('event::received', event);
            }
          }
          
          self.cal.calendar = new FullCalendar.Calendar(self.$el,args);
    
          self.cal.calendar.render();
            }
    
    
        },
        mounted () {
    
            this.init(); 
        }
        
      })
      
      let vm = new Vue({
        el: '#app',
    
        data () {
           return {
              tasks: [
                 {
                    title: 'Event1',
                    start: '2019-05-10 12:30:00',
                    end: '2019-05-10 16:30:00'
                 },
                 {
                    title: 'Event2',
                    start: '2019-05-07 17:30:00',
                    end: '2019-05-07 21:30:00'
                 }
              ],
              editable: false,
              selectedDate : moment().format('DD-MM-YYYY'),
              task: {
                title:null,
                start: '2019-05-07 17:30:00',
                end: '2019-05-07 21:30:00'
              },
              newtask: {}
           }
        },
        
    
        methods: {
    
            addTask: function(event){
                
                var d = moment().format('YYYY-MM-DD');
    
                var sel = new Date(this.selectedDate.replace( /(\d{2})-(\d{2})-(\d{4})/, "$2/$1/$3"));
    
                //console.log();
                
                var date = new Date(moment(sel).format('YYYY-MM-DD')+ 'T00:00:00'); // will be in local time
                
                this.tasks.push({
                    title: this.task.title,
                    start: date,
                    end: date
                  });
                  
                  $("#addTaskModal").modal('hide');
    
                  this.$nextTick(function () {
                    // DOM is now updated
                    // `this` is bound to the current instance
                    //this.doSomethingElse()
                  })
            },
    
            dateClicked: function(date) {
                this.selectedDate = moment(date.date).format('DD-MM-YYYY');
              },
    
            eventClicked: function(event) {
                this.selectedDate = moment(event.event.start).format('DD-MM-YYYY');
              },
    
              showObject: function() {
    
                console.log(this.tasks);
              }
        
        
        }
      })
    <link href="https://fullcalendar.io/releases/core/4.1.0/main.min.css" rel="stylesheet"/>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
    
    
    
    <script src="https://fullcalendar.io/releases/core/4.1.0/main.min.js"></script>
    <script src="https://fullcalendar.io/releases/daygrid/4.1.0/main.min.js"></script>
    <script src="https://fullcalendar.io/releases/interaction/4.1.0/main.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
    
    
    
    <div class="container mt-5" id="app">
        <div>
    
            <div class="row">
                <div class="col-md-8">
                    <full-calendar 
                    v-bind:tasks="tasks" 
                    v-on:date_clicked="dateClicked"
                    v-on:event_clicked="eventClicked"
                    :newtask="newtask"
                    ></full-calendar>
                </div>
                
            <div class="col-md-4">
              
                <div class="pb-3">
                        <span id="CurrentDate" class="" style="cursor: pointer;">{{selectedDate}}</span>
                        <span class="badge badge-primary">{{tasks.length}}</span>
                        <button @click="showObject">show object</button>
                </div>
    
              <table class="table table-condensed">
                <tbody v-for="task in tasks">
                  <tr>
                    <td>
                      <input
                        type="checkbox"
                      />
                    </td>
                    <td>{{task.title}}</td>
                    <td></td>
                  </tr>
                </tbody>
                <tbody>
                  <tr v-if="tasks.length==0">
                    <td colspan="3" class="text-center">No tasks to display</td>
                  </tr>
                </tbody>
              </table>
    
    
    
     
            </div>
            </div>
        </div>
    
        <div class="modal fade" id="addTaskModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
            <div class="modal-dialog" role="document">
              <div class="modal-content">
                <div class="modal-header">
                  <h5 class="modal-title" id="exampleModalLabel">Add a task</h5>
                  <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                  </button>
                </div>
                <div class="modal-body">
                        <div class="form-group">
                                <input type="text" id="NewTaskTitle" placeholder="Enter task name" class="form-control" v-model="task.title" />
                        </div>
                </div>
                <div class="modal-footer">
                  <button class="btn btn-success btn-sm" @click="addTask">Add</button>
                  <button data-bind="click: CancelAddNewTask" class="btn btn-seconday btn-sm" data-dismiss="modal">Cancel</button>
                </div>
              </div>
            </div>
        </div>
    </div>