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
tokenfor the access token from the backend and the usernameas well as the backend URL from the.env.developmentfile. initialiseStorethis function we run when we start the app to check if the user logged in or not. we uselocalStoragefor 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.saveLoginwe will use it when the user logged in for saving the data in the store andlocalStorageas well.Logoutis 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$vto be used foruseVuelidate()for validation. - The
SubmitButtoncomponent, 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@inputmethod on theinputfields for validation.- In
onSubmittheif (!this.v$.$invalid)to check if there are no validation errors. When we send the login datathis.isSendingForm = true;then we useaxios.postto submit the form andthis.$store.commit('saveLogin')saving the login data response if successful of.catch( (error)showing anerror 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
