In this article, we will create VueJS app of ToDo App with TailwindCSS using GraphQL API we built in previous articles.
Demo at:
Code Repo is at:
TODO APP
built by @sulmanweb
Technologies
- Docker
API (rails-api)
- Ruby on Rails 6
- MongoDB 4
- GraphQL
Vue Front End
- VueJS 2
- TailwindCSS
- FontAwesome
- Apollo GraphQL Client
To Run
- Need Docker Installed in system
- In terminal in working repo write
docker-compose up --build -d
- Rails API Playground will be at http://localhost:3000/graphiql
- Front end App can be viewed at http://localhost:8080
In the previous article, we created rails, graphql API with MongoDB:
Simple ToDo GraphQL API in Ruby on Rails and MongoDB with Docker [PART 02]
Sulman Baig ・ Aug 1 '20
Now we move to VueJS part of the project. So, first create the VueJS app with the following command in the front-vue
directory:
docker run --rm -v "${PWD}:/$(basename `pwd`)" -w "/$(basename `pwd`)" -it node:14.5-alpine sh -c "yarn global add @vue/cli && vue create . && vue add apollo"
This command will create VueJS app in the directory and add plugin of vue-apollo
which we will use for the graphql api backend access.
Then create the front-vue
service in docker and start in docker by following commands:
todo-app/docker-compose.yml
front-vue:
build: ./front-vue
ports:
- "8080:8080" # use port that you want to in your local instead of 8091
volumes:
- ./front-vue:/front-vue
- front_node_modules:/front-vue/node_modules
volumes:
front_node_modules:
todo-app/front-vue/Dockerfile
FROM node:14.5-alpine
WORKDIR /front-vue
COPY package*.json ./
RUN yarn install
COPY . .
CMD ["yarn", "serve"]
docker-compose up -d --build
Init Project:
We will use the following packages which you can see todo-app/front-vue/package.json
- Font Awesome SVG Icon package for vue
- TailwindCSS
- PurgeCSS for tailwind unused css purging
- Vue-Apollo (which we added as vue plugin)
Initialize tailwindcss and fontawesome in main.js by adding following code:
todo-app/front-vue/src/main.js
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faPlus,
faLongArrowAltLeft,
faCheckCircle,
faCircle,
faTrash,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
library.add(faPlus, faLongArrowAltLeft, faCheckCircle, faCircle, faTrash);
Vue.component("font-awesome-icon", FontAwesomeIcon);
import "./assets/styles/index.css";
Clear the App.js
and other files for code and styling. Also add purging css for production:
todo-app/front-vue/postcss.config.js
const autoprefixer = require("autoprefixer");
const tailwindcss = require("tailwindcss");
const postcssPurgecss = require(`@fullhuman/postcss-purgecss`);
const purgecss = postcssPurgecss({
// Specify the paths to all of the template files in your project.
content: ["./public/**/*.html", "./src/**/*.vue"],
// Include any special characters you're using in this regular expression.
// See: https://tailwindcss.com/docs/controlling-file-size/#understanding-the-regex
defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
// Whitelist auto generated classes for transitions and router links.
// From: https://github.com/ky-is/vue-cli-plugin-tailwind
whitelistPatterns: [
/-(leave|enter|appear)(|-(to|from|active))$/,
/^(?!(|.*?:)cursor-move).+-move$/,
/^router-link(|-exact)-active$/,
/svg.*/,
/fa.*/,
],
});
module.exports = {
plugins: [
tailwindcss,
autoprefixer,
...(process.env.NODE_ENV === "production" ? [purgecss] : []),
],
};
Also, add tailwind CSS file
todo-app/front-vue/src/assets/styles/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Change the name of token and URL of backend graphql
todo-app/front-vue/src/vue-apollo.js
// Name of the localStorage item
const AUTH_TOKEN = "token";
// Http endpoint
const httpEndpoint =
process.env.VUE_APP_GRAPHQL_HTTP || "http://localhost:3000/graphql";
GraphQL using Vue-Apollo
There are many ways to make graphql mutation and query through Vue-Apollo which I have explained in different components and views but the main way which I used everywhere and I like most is making querying and mutation in .gql
files separately and then we can change the query or mutation even after project is created separately in their own files. Also the way the bearer authorization being handled by the vue-apollo automatically is great.
So, we create a folder graphql
in src
and we add all the queries and mutations in gql language there. For example:
todo-app/front-vue/src/graphql/mutations/signUp.gql
mutation signUp($email: String!, $password: String!) {
signUp(input: { email: $email, password: $password }) {
token
}
}
todo-app/front-vue/src/graphql/queries/me.gql
query me {
me {
lists {
id
name
}
}
}
Different ways to make query and mutations
Mutations
We can use mutation as components with a template like in signUp:
todo-app/front-vue/src/views/auth/signUp.vue
<ApolloMutation
:mutation="require('@/graphql/mutations/signUp.gql')"
:variables="{ email, password }"
@done="signedUp"
>
<template v-slot="{ mutate, loading, error }">
<!-- Error -->
<div class="bg-red-100 border-l-4 border-red-400 p-4" v-if="error">
<div class="flex">
<div class="flex-shrink-0">
<svg
class="h-5 w-5 text-red-400"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class="ml-3">
<p class="text-sm leading-5 text-red-700">
{{ error.toString().replace("Error: GraphQL error: ", "") }}
</p>
</div>
</div>
</div>
<!-- Sign Up Form -->
<form action="#" class="mt-5" @submit.prevent="mutate()">
<!-- Email -->
<div class="mb-5">
<input
type="email"
name="email"
id="email"
v-model="email"
class="rounded w-full shadow border border-gray-900 tracking-wide text-xl p-2"
placeholder="Email"
autocomplete="on"
required
/>
</div>
<!-- Password -->
<div class="mb-5">
<input
type="password"
name="password"
v-model="password"
class="rounded w-full shadow border border-gray-900 tracking-wide text-xl p-2"
placeholder="Password"
id="sign-up-password"
autocomplete="off"
/>
</div>
<div class="text-center flex justify-between">
<!-- sign in link -->
<router-link
to="/sign_in"
class="self-center hover:underline hover:text-gray-800"
>Sign In</router-link
>
<!-- sign up link -->
<button
type="submit"
:disabled="loading"
class="px-4 py-2 rounded tracking-wide font-semibold bg-black text-white text-lg hover:bg-gray-800 shadow-lg"
>
Sign Up
</button>
</div>
</form>
</template>
</ApolloMutation>
Second way is to use in normal vue method like below:
todo-app/front-vue/src/views/listShow.vue
async changeStatus(task) {
const result = await this.$apollo.mutate({
mutation: require("@/graphql/mutations/changeTaskStatus.gql"),
variables: {
id: task.id,
},
});
if (!!result.data) {
await this.loadList();
}
},
Queries
The first way is graphql query as a component like we saw in mutations.
Second way is the same to call in function same as that of mutations like:
todo-app/front-vue/src/views/listShow.vue
async getList() {
return await this.$apollo.query({
query: require("@/graphql/queries/showList.gql"),
variables: {
id: this.$route.params.listId,
},
fetchPolicy: "network-only",
});
},
Third way is to call query in apollo object in the js section of the vue component like:
todo-app/front-vue/src/views/Home.vue
apollo: {
me: {
query: require("@/graphql/queries/me.gql"),
result({ data }) {
if (data) {
this.lists = data.me.lists;
this.loading = false;
}
},
},
},
See the Complete Development in Videos
Happy Coding!
Top comments (0)