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 .