In the first part of this tutorial, we created a fictional employee management system using Django and Graphene which allowed us to see all employees, create new entries as well as updating and deleting existing entries.
In this second part, we will continue from where we left off and build a simple VueJS application which we can use to view our employees and filter by cities.
Prerequisites
In order to start working with vueJS and other front end tools, one of the first things we need is a runtime. The most popular javascript runtime is NodeJS which we need for this project.
So head on to the NodeJS website and download the version for your machine. Once the installation is completed, open your terminal and confirm that it exists. Type node -v and you should get a response with a version number
$ node -v
v11.10.0
NodeJS comes packaged with npm which is a javascript package manager which essentially makes it easy for developers to download and install needed packages (similar to pip in python). You can also confirm that it is installed by typing the command below
$ npm -v
6.7.0
Lastly, we want to install vueJS and vue-cli. This is where the advantage of npm comes in
sudo npm install -g @vue/cli
Adding sudo to your vueJS install statement is not mandatory but you may get an error in some cases, especially if you are not logged in as an admin on your computer. Once that download is complete, you can confirm the installation by just typing “Vue” in your terminal
$ vue
Usage: vue <command> [options]
Options:
-V, — version output the version number
-h, — help output usage information
Commands:
create [options] <app-name> create a new project powered by vue-cli-service
add [options] <plugin> [pluginOptions] install a plugin and invoke its generator in an already created project
invoke [options] <plugin> [pluginOptions] invoke the generator of a plugin in an already created project
inspect [options] [paths…] inspect the webpack config in a project with vue-cli-service
serve [options] [entry] serve a .js or .vue file in development mode with zero config
build [options] [entry] build a .js or .vue file in production mode with zero config
ui [options] start and open the vue-cli ui
init [options] <template> <app-name> generate a project from a remote template (legacy API, requires @vue/cli-init)
config [options] [value] inspect and modify the config
upgrade [semverLevel] upgrade vue cli service / plugins (default semverLevel: minor)
info print debugging information about your environment
Run vue <command> — help for detailed usage of given command.
Now we’re ready to begin
1). Navigate to a location on your local drive where you want to create your project and create a folder for your application. In my case, I’m going to my desktop and creating a directory called “vueapps” and changing into the directory
$ cd ~/Desktop
$ mkdir vueapps && cd vueapps
I will now create a new vueJS app called “company”
$ sudo vue init webpack company
You will go through a number of prompts similar to the image below. Select your preferences as required.
After the installation is completed, you should now see a directory with the same name as your project. When you change into the directory, you should see the following files and directories
$ cd company
2). The next step will be to install all of the plugins that were bundled with vueJS and vue-cli. If you open the “package.json” file, you will see a list of all the plugins and dependencies
To install all of these dependencies, run “npm install”. Make sure that you are inside the company app directory
$ sudo npm install
Just a side note that all of the plugins will be installed within the node_modules subdirectory. If everything runs fine, you should see a message similar to below (may be more or less packages depending on your system)
audited 11689 packages in 5.466s
found 0 vulnerabilities
Now we can create a development server to start working on our application within the local computer
$ npm run dev
I Your application is running here: http://localhost:8080
You can now navigate to the localhost IP provided and you should see a screen similar to the image below
Now, we are ready to start building our application
3). If you are familiar with Vue, you will know that Vue is akin to a single page application (SPA) which is made up of multiple components and routes.
Majority of our code will exist in the “src” directory where all of the components for our application will reside in the “components” subdirectory.
The demo page (http://localhost:8080) that we navigated to after our installation is a product of the HelloWorld.vue file (component). Each component is made up of a template tag, a script tag and a style tag.
However, the App.vue file is parent SPA file for the application where all components and routers need to be registered in order for them to be displayed within the browser. Even though all of the data that is displayed in the browser is created and exists in the HelloWorld.vue file, it needs to be registered in the App.Vue file or else it will not be displayed
Now we will need to create a new component to visualize our company data. Make sure that you are within the src/components directory and create a new component
$ cd src/components
$ sudo touch Comp.vue
4). Open up the new Comp.vue file and start with a simple component where the root div in the template simply prints out “Hello World”, an empty function and no style.
We also need to add a selector (CompanyData) within the script tag and register it in the App.vue file in order for the component to work.
<template>
<div>
<p>Hello World</p>
</div>
</template>
<script>
export default{
name: 'CompanyData',
data(){
return {}
}
}
</script>
<style scoped>
</style>
Comp.vue
5). Now, we have to go to the App.Vue file to register this new component. Notice the new line additions (lines 5, 11 & 17)
<template>
<div id="app">
<img src="./assets/logo.png">
<HelloWorld/>
<CompanyData/> <!-- Added New tag -->
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld'
import CompanyData from './components/Comp' //import new component
export default {
name: 'App',
components: {
HelloWorld,
CompanyData //register new component
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
App.vue
Let’s view our changes by running the dev server again and navigate to the browser
$ npm run dev
You should now see your “Hello World” component at the bottom of the page. Success!
Now we can start working to organize and visualize the data from our django application.
6). For our company application, we want to be able to list all of the current employees within the company as well as having the ability to filter by city. In order to make this visually appealing and easily readable, we’ll use a bootstrap table to display all the pertinent data.
First of all, we will install bootstrap
$ npm install bootstrap
7). I won’t dive too much into the styling aspects since this is not a webpage design tutorial and I assume that if you are reading this, you have a pretty good grasp of CSS.
For my page:
<template>
<div id="app">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>
<!-- <HelloWorld/> -->
<CompanyData/> <!-- Added New tag -->
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld'
import CompanyData from './components/Comp' //import new component
export default {
name: 'App',
components: {
HelloWorld,
CompanyData //register new component
}
}
</script>
<style>
@import "../node_modules/bootstrap/dist/css/bootstrap.min.css"; /*Importing Bootstrap*/
#app {
/* font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;*/
}
</style>
App.vue
You can now see that all that is left on the page is the new navbar and the our “Hello World” comment
Hope your page looks similar to mine.
We finally get to the fun stuff! We get to connect our Vue application to the GraphQL API.
8). We will need to install axios in order to make HTTP requests from our application.
npm install axios
In order to continue, you have to make sure that your django application from part 1 of this tutorial is running. If not, navigate to the directory and run python manage.py runserver. Navigate to http://127.0.0.1:8000/graphql to confirm that Django app is running
9). Let’s make some edits to the Comp.vue file to be able to GET data from the API.
<template>
<div>
<p>Hello World</p>
{{ directory }}
</div>
</template>
<script>
import axios from 'axios'
export default{
name: 'CompanyData',
data(){
return {
directory: []
}
},
async mounted () {
try {
var result = await axios({
method: 'POST',
url: 'http://127.0.0.1:8000/graphql/',
data: {
query: `
{
allTitles {
edges {
node {
id
titleName
}
}
}
}
`
}
})
this.directory = result.data.data.allTitles
} catch (error) {
console.error(error)
}
}
}
</script>
<style scoped>
</style>
Comp.vue
Save and refresh your Vue app now.
10). Let’s see if we can see the returned data in the browser console now. Open “inspect element” on your browser and go to the console tab.
If you see an error mentioning that your request has been blocked by a CORS policy, don’t be alarmed. I actually did that on purpose.
If you’ve worked on any application which requires you to make HTTP requests, you’ve most likely hit a CORS error at least once in your lifetime. This is usually the case when a web application makes a request from a resource that has a different origin (domain, protocol, port) from its own. In our case, even though both applications are running on localhost, they are running on two different ports.
11). In order to solve this, we have to make some slight changes to our django application. First of all, we need to install django-cors-headers
pipenv install django-cors-headers
Add “corsheaders” to the list of installed apps in the settings.py file
INSTALLED_APPS = [
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
‘graphene_django’,
‘company’,
‘corsheaders’, #added for cors
]
Add “corsheaders.middleware.CorsMiddleware” to the middleware list in the settings.py file
MIDDLEWARE = [
‘corsheaders.middleware.CorsMiddleware’, #added for cors
‘django.middleware.security.SecurityMiddleware’,
‘django.contrib.sessions.middleware.SessionMiddleware’,
‘django.middleware.common.CommonMiddleware’,
‘django.middleware.csrf.CsrfViewMiddleware’,
‘django.contrib.auth.middleware.AuthenticationMiddleware’,
‘django.contrib.messages.middleware.MessageMiddleware’,
‘django.middleware.clickjacking.XFrameOptionsMiddleware’,
]
Lastly, add a whitelist of all inbound urls that will be making requests to the GraphQL server to the bottom of the settings.py file
CORS_ORIGIN_WHITELIST = (
‘localhost:8080/’
)
Now, let’s restart your Django app && restart your Vue app and try again.
The error should now be gone and now we can see data returned (even though it does not look pretty).
12). In order to make the data look more presentable, I’m going to add a bootstrap table to display the data. I’m also going to use this opportunity to change the GraphQL query to return all employees in the company (AllEmployees)
<template>
<div>
<table class="table table-striped mt-4">
<thead>
<tr>
<th scope="col">name</th>
<th scope="col">title</th>
<th scope="col">city</th>
</tr>
</thead>
<tbody>
<tr v-for="input in directory.edges" :key="input.id">
<td>{{ input.node.employeeName }}</td>
<td>{{ input.node.employeeTitle.titleName }}</td>
<td>{{ input.node.employeeCity.cityName }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import axios from 'axios'
export default{
name: 'CompanyData',
data(){
return {
directory: []
}
},
async mounted () {
try {
var result = await axios({
method: 'POST',
url: 'http://127.0.0.1:8000/graphql/',
data: {
query: `
{
allEmployees {
edges {
node {
id
employeeName
employeeTitle {
titleName
}
employeeCity {
cityName
}
}
}
}
}
`
}
})
this.directory = result.data.data.allEmployees
} catch (error) {
console.error(error)
}
}
}
</script>
<style scoped>
</style>
Comp.vue
Now this looks a lot better and more presentable!!
While this looks great. This is only returning data and is not making great usage of our GraphQL server.
13). Let’s add a search function to our app to allow users to be able to search for employees using the city.
<template>
<div>
<form action="" @submit.prevent="mounted()">
<div class="col-sm-6">
<input v-model="city" city="" type="text" class="form-control">
<button type="submit" name="button">Submit</button>
</div>
</form>
<table class="table table-striped mt-4">
<thead>
<tr>
<th scope="col">name</th>
<th scope="col">title</th>
<th scope="col">city</th>
</tr>
</thead>
<tbody>
<tr v-for="input in directory.edges" :key="input.id">
<td>{{ input.node.employeeName }}</td>
<td>{{ input.node.employeeTitle.titleName }}</td>
<td>{{ input.node.employeeCity.cityName }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import axios from 'axios'
export default{
name: 'CompanyData',
data(){
return {
city: '',
directory: []
}
},
methods: {
async mounted () {
try {
var result = await axios({
method: 'POST',
url: 'http://127.0.0.1:8000/graphql/',
data: {
query: `
{
allEmployees(employeeCity_CityName: "`+this.city+`") {
edges {
node {
id
employeeName
employeeTitle {
titleName
}
employeeCity {
cityName
}
}
}
}
}
`
}
})
this.directory = result.data.data.allEmployees
} catch (error) {
console.error(error)
}
}
}
}
</script>
<style scoped>
</style>
Comp.vue
You should now be able to see a search box at the top of the page but you will also notice that the table is not returning any data anymore. This is because it now requires you enter a city name for it to return results.
I’ll enter “New York” in the search box and hit ENTER
Same for “Miami”
Looking great now!!
You should now be able to add more data to the django server and retrieve it from the VueJS frontend.
Thank you for taking the time to read through the two parts of this tutorial and apologies for the long wait in completing the series. If you want to see the repo for this project, you can click here
☞ 5 ways to use Vue DevTools for Quicker and more Efficient Debugging
☞ Vue.js Testing Guide: Unit testing in Vue and Mocha
☞ How to integrate the Smart Chart component in Vue.js Application
Good luck!
☞ Try Django Tutorial - Build a Web Application with Python Framework
☞ Learn GraphQL with Laravel and Vue.js - Full Tutorial
☞ Python Django Web Framework - Full Course for Beginners
☞ Django and VueJS Full Stack Course For Beginners [ How to Become Full Stack Developer ]
☞ Create a Fullstack app with Django, GraphQL and VueJS (Part 1)