Building a Horizontal Calendar with VueJS and Vis.js Part 3/3

Casper Bottelet
3 min readJun 23, 2020

--

Part 3 Getting and updating data with the backend.

This is gonna be a little more difficult to show off as it depends on how your data is structured but let’s give it a go.

This is how I have the data structure, I’ll try and not clutter it with too much information that isn’t gonna be used:

Appointments:* external_id* title* user_id* start_at* end_atUsers:* name* department_idDepartments:* name

I’ll have a basic endpoint that returns appointments as JSON and an endpoint that returns the users, with the user -> department relation, this can be made with any backend but in this case, we are using Laravel:

I will not be showing the Users endpoint as it should be reasonably simple, and the same process as for appointments I’m just returning all users in JSON format

app/Http/Controllers/AppointmentsController.phppublic function appointments(){return Appointment::with([‘user:id,name,external_id’, ‘user.department:name’])->get();}and i have registered a route for itroutes/web.phpRoute::get(‘appointments/data’, ‘AppointmentsController@appointments’)->name(‘appointments.data’);

This will return all the appointments. I can then retrieve these JSON values in my Vue component by using Axios which lets you do HTTP requests, once I retrieved all the data I loop through it to map the values that correspond with Vis.js data structure. I do this with Vuejs created method this way:

created() {axios.get(‘/users/users’).then((res) => {res.data.forEach((user) => {let group = {};group.id = user.external_id;group.content = “<div style=’float:left; margin-right: 10px;’><img src=’https://picsum.photos/400' style=’border-radius:50%;’ width=’60em’></div>” +“<div style=’float:right;’><p style=’margin: 0'>” + user.name + “</p>” +“<span style=’font-size:11px; font-weight: 300; font-color:#eee;’>” + user.department[0].name + “<span></div>”;group.style = “width: 240px”;this.groups.push(group);})});axios.get(‘/appointments/data’).then((res) => {res.data.forEach((appointment) => {//Create an group from the response and pushes it to Vuejs datalet group = {};group.id = appointment.user.external_id;//You can replace the content = appointment.user.name with HTML example group.content = “<p>” + appointment.user.name + “</p>”group.content = appointment.user.name;group.style = “width: 240px”;this.items.push(item);})});},

One obstacle you might notice is that the view is rendered before the data is loaded, this is fixed by using `async mounted` let’s start by creating a new data property declared `dataLoaded: null` also add `this.dataLoaded = axois.get(…)` in your created method

now change your mounted method into

async mounted() {await this.dataLoaded……}

This is how everything should look like:

created() {axios.get(‘/users/users’).then((res) => {res.data.forEach((user) => {let group = {};group.id = user.external_id;group.content = user.name ;this.groups.push(group);})});this.dataLoaded = axios.get(‘/appointments/data’).then((res) => {res.data.forEach((appointment) => {let item = {};item.id = appointment.external_id;item.group = appointment.user.external_id;item.content = appointment.title;item.start = appointment.start_at;item.end = appointment.end_at;this.items.push(item);})});},async mounted() {await this.dataLoaded// Create a Timelinelet timeline = new Timeline(document.getElementById(‘visualization’));timeline.setOptions(this.options);timeline.setGroups(this.groups);timeline.setItems(this.items);}

This also gives you the option to add a loading icon in case you have a lot of data needed to be fetched, but it’s out of scope for this post.

Now you are retrieving your data from the backend! Let’s for the last part update data when an entry/item is dragged across the timeline.

We will be looking at the `onMove` function from the options item, instead of printing to the console, we will now make an Axois HTTP POST request, change your onMove method.

onMove: function (item, callback) {console.log(item, callback)if (item.content != null) {callback(item); // send back adjusted item}else {callback(null); // cancel updating the item}},To:onMove: function (item, callback) {if (item.content != null) {axios.post(‘/appointments/update/’ + item.id, item)}else {callback(null); // cancel updating the item}}

This will make a POST request to an endpoint which just updates the appointment:

public function update(Request $request, Appointment $appointment){$appointment->start_at = Carbon::parse($request->start);$appointment->end_at = Carbon::parse($request->end);$appointment->user()->associate(User::where(‘external_id’, $request->group)->first());$appointment->save();return response($appointment);}

Bam! Now you can update your entries by dragging and dropping!

For the full Vuejs code you can find a gist here:

https://gist.github.com/Bottelet/9eda4b6b3d587635c5ad7c7bc1d8132d

That will be all for now. If you have any questions you can reach out on Twitter @cbottelet

Linkedin Casper

Github Bottelet

--

--