2018-03-08
I've been recently asked to build up a little vue
example.
The task is, to show appointments from a google calendar.
I know some react
stuff, but the vue
framework and the google api was new for me.
So I start with some digging.
First things first.
The vue
example will be a client only solution.
As like create-react-app
, I use vue-cli to bootstrap my application.
I use vim on a daily base.
Editing vue
files is similar to editing react
files.
You can have a mixture of javascript and templates.
There is a vim plugin,
Plugin 'posva/vim-vue'
which helps editing vue
files.
I also update the linter, to get error information from syntastic
$ npm i -g eslint eslint-plugin-vue
After installing the vue cli tool,
$ npm install -g @vue/cli
I can bootstrap my appointment
application with
$ vue create appointment
A git repo is initialized with the current logged on user credentials.
With
$ npm run build
a minified version of the application is put into the dist
folder.
$ npm run serve
will build and run the application on port 8080
.
This will be used during development.
To get access to the google calendar, I have to create a project in the google developer console.
I need an oauth2 id
for my application.
The authorized javascript sources
must be configured for the oauth2 id
.
The example from the google api documentation works as expected.
The data
is stored in eventResult
.
data: function() {
return { eventResult : [] }
}
The root component will hold the state of appointment
.
const CLIENT_ID = process.env.VUE_APP_CLIENT_ID;
const DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
const SCOPES = "https://www.googleapis.com/auth/calendar.readonly";
The process.env
part will be replaced during build process with the actual client id.
A vue component can have methods, which are accessible in lifecycle hook functions
gapiLoad: function() {
gapi.load('client:auth2', this.initClient)
}
will load the client and the oauth part of the google api.
The initClient
function is passed with the credentials.
initClient: function() {
gapi.client.init({
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
}).then(() => {
gapi.auth2.getAuthInstance().isSignedIn.listen(this.signedIn);
// get signed in status on startup
this.signedIn(gapi.auth2.getAuthInstance().isSignedIn.get());
})
}
The signedIn
function will load the data from the google api,
signedIn: function(isSignedIn) {
if (isSignedIn) {
gapi.client.calendar.events.list({
'calendarId': 'primary',
'timeMin': (new Date().toISOString(),
'showDeleted': false,
'singleEvents': true,
'maxResults': 10,
'orderBy': 'startTime'
}).then((response) => {
this.eventResult = response.result.items;
})
} else {
this.eventResult = [];
}
}
and stores the result in eventResult
.
These functions will be triggered after the component is mounted.
mounted: function() {
this.gapiLoad();
}
At this point the data will be fetched, if the user gives the permission to load data from his primary calendar.
The data for the component will be delivered via props. The component it self will be stateless. This means, the component depends only on the props, delivered on start up. When the underlying data changes, the view will rerender.
For the visual, I use vue material.
The display component will be a simple table with some data from the response.
For displaying dates, I use momentjs.
I first create a method, to make momentjs
accessible within the template.
methods: {
moment: function () {
return moment();
}
}
To reduce the amount of code within the templates, filters can be used.
filters: {
moment: function (date) {
if (date) {
return moment(date).locale('en').format('lll');
}
return '';
}
}
moment
will be called, only when date
is not empty.
The resulting template looks like
<template>
<div>
<md-table v-model="gridData" md-card>
<md-table-toolbar>
<h1 class="md-title">Events</h1>
</md-table-toolbar>
<md-table-row slot="md-table-row" slot-scope="{ item }">
<md-table-cell md-label="Start">{{ item.start.dateTime | moment }}</md-table-cell>
<md-table-cell md-label="End">{{ item.end.dateTime | moment }}</md-table-cell>
<md-table-cell md-label="Created">{{ item.created | moment }}</md-table-cell>
<md-table-cell md-label="Summary"><a :href="item.htmlLink" target="_blank">{{ item.summary }}</a></md-table-cell>
<md-table-cell md-label="Creator">{{ item.creator.displayName }}</md-table-cell>
<md-table-cell md-label="Organizer">{{ item.organizer.displayName }}</md-table-cell>
</md-table-row>
</md-table>
</div>
</template>
If you like, you can take a look at the result and at the sources.