Let’s set up Vue.js Admin Panel and create login functionality with CoreUI, the only Open Source Bootstrap And Vue 3 Admin Dashboard.
What is CoreUI? And Why?
Because it’s Bootstrap Admin Dashboard Template, Vue 3 UI Components Library, mainly having everything ready to start developing your admin application. I have used it before but the React version, it’s saved so much time. So I have decided to give it a shot on Vue.js. I have built Vue 3 admin panel and used it in a couple of projects but I have wasted my time in inventing the wheel. From A full stack perspective, We should capture any chance of saving time.
CoreUI Installation.
I forked the CoreUI repository on GitHub. If you don’t know how to fork it, at the top right you will find a button named “fork” with a number next to it, Just click the little arrow and click “create a new fork” from the drop-down menu. Now the repository has been created on your account. You can clone it to your local machine. I’m using Windows 10 and Nodejs 18.
In the Root folder of the project run the below command to install the necessary packages.
npm install
Let’s test our installation by running the server and open the gaven URL in the browser, for me it was http://localhost:8080/
. It should open the dashboard home page.
npm run serve
Adding Authentication Functions In Vuex Store.
Vue applications by default support .env
file. so let’s create one in the project root folder. for development in the root folder add .env.development
. Now we will create two variables one for the backend URL and another for Base URL of the admin app.
I’m using laravel as backend you can watch the tutorial of Laravel Authentication on my youtube channel or article here
VUE_APP_BACKEND_URL= http://127.0.0.1:8000/api/en
BASE_URL = http://localhost:8080/
Setting up the store src\store\index.js
and add the below code or functions next to the existed theme codes in the file.
import { createStore } from 'vuex'
/* eslint-disable */
export default createStore({
state: {
//
// Add
token:'', //access token from backend
name:'', // the user name from backend
backendUrl: process.env.VUE_APP_BACKEND_URL,
},
mutations: {
//
// Add
initialiseStore(state) {
if(localStorage.getItem('token')){
state.token = localStorage.getItem('token');
}
if(localStorage.getItem('name')){
state.name = localStorage.getItem('name');
}
if(state.token == ""){
return false;
}
return true;
},
saveLogin(state,LoginData){
state.token =LoginData.token;
state.name =LoginData.name;
localStorage.setItem('token', LoginData.token);
localStorage.setItem('name', LoginData.name);
},
Logout(state){
state.token ="";
state.name ="";
localStorage.removeItem('token');
localStorage.removeItem('name');
}
},
//
})
- In the state object, we add the globe variable that we will be using across the app and it will affect the app. such as
token
for the access token from the backend and the username
as well as the backend URL from the.env.development
file. initialiseStore
this function we run when we start the app to check if the user logged in or not. we uselocalStorage
for saving the login data in the browser for the next time otherwise, when the user closes the app the login data will be lost and the user must log in again.saveLogin
we will use it when the user logged in for saving the data in the store andlocalStorage
as well.Logout
is for removing all the logged-in data so that the user will be logged out.
Vue router Check Login Middleware
We need to redirct the user to login page if not logged in or logged out.
First of all we need to initiate our vuex store to check if the login status via adding initialiseStore
to src\App.vue
<template>
<router-view />
</template>
<script>
export default {
mounted() {
this.$store.commit('initialiseStore')
},
}
</script>
<style lang="scss">
// Import Main styles for this application
@import 'styles/style';
</style>
Create a new folder name it “auth “and copy page\Login.vue
file to it, we will added later but now let’s modify the login route and add route rule so that if the user is not logging, the router
redirect to login page.
import store from '@/store'
//
{
path: 'login',
name: 'Login',
component: () => import('@/views/auth/Login'), // modify here
},
//at the end of the file
router.beforeEach((to, from, next) => {
let token = store.state.token
console.log('router ==' + token)
if (to.name !== 'Login' && !token) next({ name: 'Login' })
else next()
})
export default router
Login Page
Let’s install axios
and vue-validation
for call the Login API form the backend and login form validation.
npm install axios
npm install @vuelidate/core @vuelidate/validators
Let add the script for login page.
<script>
/* eslint-disable */
import axios from 'axios'
import useVuelidate from '@vuelidate/core'
import { required, email } from '@vuelidate/validators'
import SubmitButton from '@/components/SubmitButton.vue'
export default {
name: 'Login',
setup() {
return { v$: useVuelidate() }
},
components:{SubmitButton},
data() {
return {
form: {
email: '',
password: '',
},
errorMgs: '', // to show error message
ShowError: false, // flag to toggle errorMgs
isSendingForm: false,
}
},
validations() {
return {
form: {
email: {
required,
email,
},
password: {
required,
},
},
}
},
methods: {
setTouched(theModel) {
if(theModel == 'email' || theModel == 'all' )
{this.v$.form.email.$touch()}
if(theModel == 'password' || theModel == 'all')
{this.v$.form.password.$touch()}
},
onSubmit(event) {
event.preventDefault()
this.setTouched('all');
if (!this.v$.$invalid)
{
this.isSendingForm = true;
axios.post(
this.$store.state.backendUrl+'/login',this.form,
{
headers: {"Content-Type": "application/json",}
})
.then((response) => {
console.log(response);
// saving the login data after success
this.$store.commit('saveLogin',
{
"token":response.data.token,
"name":response.data.name,
});
this.$router.push('/')
this.isSendingForm = false;
})
.catch( (error) => {
// login failed
console.log(error);
this.ShowError=true;
this.errorMgs = error.response.data.error;
this.isSendingForm = false;
});
}
},
},
}
</script>
- In
setup()
we declare$v
to be used foruseVuelidate()
for validation. - The
SubmitButton
component, we will create later. - Then we add the form data object in addition to the flags for showing error messages and sending the form.
validations()
for setting up the form validation inputs.setTouched(theModel)
We use it with@input
method on theinput
fields for validation.- In
onSubmit
theif (!this.v$.$invalid)
to check if there are no validation errors. When we send the login datathis.isSendingForm = true;
then we useaxios.post
to submit the form andthis.$store.commit('saveLogin')
saving the login data response if successful of.catch( (error)
showing an
error if failed.
Let’s add our HTML
Creating submit button with spinner src\components\SubmitButton.vue
<template>
<!-- eslint-disable -->
<CButton color="primary" type="submit" class="px-4">
<div v-if="isSendingForm" class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span>
</div>
{{title}}
</CButton>
</template>
<script>
/* eslint-disable */
export default{
name: "SubmitButton",
props: ['title','isSendingForm'],
}
</script>
Adding the login page HTML
<template>
<!-- eslint-disable -->
<div class="bg-light min-vh-100 d-flex flex-row align-items-center">
<CContainer>
<CRow class="justify-content-center">
<CCol :md="8">
<CCardGroup>
<CCard class="p-4">
<CCardBody>
<CAlert color="danger"
:visible="ShowError"
dismissible @close="() => { ShowError = false }">
{{ errorMgs }}
</CAlert>
<CForm @submit="onSubmit">
<h1>Login</h1>
<p class="text-medium-emphasis">
Sign In to your account
</p>
<CInputGroup class="mb-3">
<CInputGroupText>
<CIcon icon="cil-user" />
</CInputGroupText>
<CFormInput
v-model.trim="form.email"
@input="setTouched('email')"
type="email"
placeholder="Email"
autocomplete="email"
feedback="Please provide a valid email."
:invalid="v$.form.email.$error"
/>
</CInputGroup>
<CInputGroup class="mb-4">
<CInputGroupText>
<CIcon icon="cil-lock-locked" />
</CInputGroupText>
<CFormInput
v-model.trim="form.password"
@input="setTouched('password')"
type="password"
placeholder="Password"
autocomplete="current-password"
feedback="Please provide a valid password."
:invalid="v$.form.password.$error"
/>
</CInputGroup>
<CRow>
<CCol :xs="6">
<SubmitButton
title="Login"
:isSendingForm="isSendingForm"
/>
</CCol>
<CCol :xs="6" class="text-right">
<CButton color="link" class="px-0">
Forgot password?
</CButton>
</CCol>
</CRow>
</CForm>
</CCardBody>
</CCard>
<CCard class="text-white bg-primary py-5" style="width: 44%">
<CCardBody class="text-center">
<div>
<h2>Sign up</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua.
</p>
<CButton color="light" variant="outline" class="mt-3">
Register Now!
</CButton>
</div>
</CCardBody>
</CCard>
</CCardGroup>
</CCol>
</CRow>
</CContainer>
</div>
</template>
We added the error message inside alert component CAlert
. In CFormInput
we adding the v-model for the email and password as well as the @input
to setTouched
so that we can validate the inputs. In feedback
attribute, we add the error validation message. Finally we show the invalid error on the input via :invalid
. The SubmitButton
that’s a reusable component that should be used with all forms.
Logout
Let add the logout button, If you click the user image at the top right you will find logout link in the dropdown menu. So let’s add the logout method in src\components\AppHeaderDropdownAccnt.vue
<CDropdownItem @click="logout()"> <CIcon icon="cil-lock-locked" /> Logout </CDropdownItem>
methods: {
logout() {
this.$store.commit('Logout')
this.$router.push('/login')
},
},
That’s all for now I will continue building this admin panel in a series of tutorials here. thank you.
Next tutorial: Vue.js Admin Panel Tutorial | Reset Password