Let’s create forget password & reset password page for CoreUI, the Open Source Bootstrap And Vue 3 Admin Dashboard template.
This is the second tutorial of this series, You can see the first tutorial: Vue.js Admin Panel Tutorial | CoreUI Setup And Login API.
What will we do?
- Creating two pages forget password and reset password with routes.
- We will create the required forms with validation and Axios HTTP requests.
- Handling responses and success.
Routes
We need to create two pages in src\views\auth
, ForgetPassword.vue
and ResetPassword.vue
. We leave them empty for now
In the src\router\index.js file we will add the routes for our new pages.
{
path: 'forget-password',
name: 'ForgetPassword',
component: () => import('@/views/auth/ForgetPassword'),
},
{
path: 'reset-password/:email',
name: 'ResetPassword',
component: () => import('@/views/auth/ResetPassword'),
},
In the previous tutorial, we created a middleware for only logged-in users who can navigate our app. Now, we will need some routes to be exceptional for public users. Let’s modify our middleware and create a new array for exceptional route names.
const except = ['ForgetPassword', 'ResetPassword']
router.beforeEach((to, from, next) => {
let token = store.state.token
console.log('router ==' + token)
if (to.name !== 'Login' && !token && !except.includes(to.name))
next({ name: 'Login' })
else next()
})
We will add the forget password page link to src\views\auth\Login.vue
at Forgot password?
//
//
<CCol :xs="6" class="text-right">
<router-link color="link" class="px-0" :to="{ name: 'ForgetPassword'}">
Forgot password?
</router-link>
</CCol>
//
//
Now when we click Forgot password?
, we should go to the “forget password” page src\views\auth\ForgetPassword.vue
.
Forget Password Page
Here, we will create a form that takes the user’s email to send OTP and then redirect to the reset password page after a response with success.
<script>
import axios from 'axios'
import useVuelidate from '@vuelidate/core'
import { required, email } from '@vuelidate/validators'
import SubmitButton from '@/components/SubmitButton.vue'
export default {
setup() {
return { v$: useVuelidate() }
},
name: 'ForgetPassword',
components: { SubmitButton },
data() {
return {
form: {
email: '',
},
errorMgs: '',
ShowError: false,
isSendingForm: false,
}
},
validations() {
return {
form: {
email: {
required,
email,
},
},
}
},
methods: {
setTouched(theModel) {
if (theModel === 'email' || theModel === 'all') {
this.v$.form.email.$touch()
}
},
onSubmit(event) {
event.preventDefault()
this.setTouched('all')
if (!this.v$.$invalid) {
this.isSendingForm = true
axios
.post(
this.$store.state.backendUrl + '/password/forgot-password',
this.form,
{ headers: { 'Content-Type': 'application/json' } },
)
.then((response) => {
console.log(response)
this.$router.push({
name: 'ResetPassword',
params: { email: this.form.email },
})
this.isSendingForm = false
})
.catch((error) => {
console.log(error)
this.ShowError = true
this.errorMgs = error.response.data.message
this.isSendingForm = false
})
}
},
},
}
</script>
As you can see we import axios
and vuelidate
for validating our form and of course, the submit button we create in the previous tutorial.
We have created some flags to hide and display HTML elements. validations()
and setTouched(theModel)
for validating use.
onSubmit(event)
after success for redirect to the reset password page with the form.email
as parameter
Below is the template part of the page.
<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="9" :lg="7" :xl="6">
<CCard class="mx-4">
<CCardBody class="p-4">
<CAlert
color="danger"
:visible="ShowError"
dismissible
@close="
() => {
ShowError = false
}
"
>
{{errorMgs}}
</CAlert>
<CForm @submit="onSubmit">
<h1>Forget Password</h1>
<p class="text-medium-emphasis">Enter your E-mail</p>
<CInputGroup class="mb-3">
<CInputGroupText>@</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>
<div class="d-grid">
<SubmitButton
title="Send Reset Email"
:isSendingForm="isSendingForm"
/>
</div>
</CForm>
</CCardBody>
</CCard>
</CCol>
</CRow>
</CContainer>
</div>
</template>
Reset Password
Let’s add the reset password code src\views\auth\ResetPassword.vue
It’s mostly everything in forget password page. the new things are adding 3 new inputs for otp
, password
, and password confirmation. and resend otp
email.
<script>
/* eslint-disable */
import axios from 'axios'
import useVuelidate from '@vuelidate/core'
import { required, email, sameAs, minLength } from '@vuelidate/validators'
import SubmitButton from '@/components/SubmitButton.vue'
export default {
setup() {
return { v$: useVuelidate() }
},
name: 'ForgetPassword',
components: { SubmitButton },
data() {
return {
form: {
email: this.$route.params.email,
otp: '',
password: '',
confirmPassword: '',
},
errorMgs: '',
ShowError: false,
show: true,
isSendingForm: false,
isResendingEmail: false,
ShowSuccess:false,
successMgs: 'Your password has been reset successful',
}
},
validations() {
return {
form: {
email: {
required,
email,
},
otp: {
required,
},
password: {
required,
minLength: minLength(6),
},
confirmPassword: {
sameAsPassword: sameAs(this.form.password),
},
},
}
},
methods: {
setTouched(theModel) {
if (theModel === 'email' || theModel === 'all') {
this.v$.form.email.$touch()
}
if (theModel === 'otp' || theModel === 'all') {
this.v$.form.otp.$touch()
}
if (theModel === 'password' || theModel === 'all') {
this.v$.form.password.$touch()
}
if (theModel === 'confirmPassword' || theModel === 'all') {
this.v$.form.confirmPassword.$touch()
}
},
onSubmit(event) {
event.preventDefault()
this.setTouched('all')
if (!this.v$.$invalid) {
this.isSendingForm = true
axios
.post(
this.$store.state.backendUrl + '/password/reset',
this.form,
{ headers: { 'Content-Type': 'application/json' } },
)
.then((response) => {
console.log(response)
this.show = false
this.ShowSuccess = true
this.isSendingForm = false
})
.catch((error) => {
console.log(error)
this.ShowError = true
this.errorMgs = error.response.data.error.message
this.isSendingForm = false
})
}
},
resendReset(event) {
event.preventDefault()
this.isResendingEmail = true
axios
.post(
this.$store.state.backendUrl + '/password/forgot-password',
this.form,
{ headers: { 'Content-Type': 'application/json' } },
)
.then((response) => {
console.log(response)
this.isResendingEmail = false
})
.catch((error) => {
console.log(error)
this.ShowError = true
this.errorMgs = error.response.data.message
this.isResendingEmail = false
})
}
},
}
</script>
Notice that we used sameAs
, minLength
for validation rule validations()
for password confirmation and a minimum length of the password. we get the email from the URL via this.$route.params.email
. resendReset(event)
for resending the otp
email by the email parameter in the URL. Below is the HTLM part.
<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="9" :lg="7" :xl="6">
<CCard class="mx-4">
<CCardBody class="p-4">
<CAlert
color="success"
:visible="ShowSuccess"
dismissible
@close="
() => {
ShowSuccess = false
}
"
>
{{successMgs}}
- <router-link :to="{ name: 'Login'}">
Login
</router-link>
</CAlert>
<CAlert
color="danger"
:visible="ShowError"
dismissible
@close="
() => {
ShowError = false
}
"
>
{{errorMgs}}
</CAlert>
<CForm v-if="show" @submit="onSubmit">
<h1>Reset Password</h1>
<p class="text-medium-emphasis">Use the OTP code that was sent to your email to reset your password
- <CButton @click="resendReset" color="link" class="px-0">
Resend
<div v-if="isResendingEmail" class="spinner-border spinner-border-sm" role="status"> </div>
</CButton>
</p>
<CInputGroup class="mb-3">
<CInputGroupText>@</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-3">
<CInputGroupText>
<CIcon icon="cil-shield-alt" />
</CInputGroupText>
<CFormInput
v-model.trim="form.otp"
@input="setTouched('otp')"
type="number"
placeholder="OTP Code"
feedback="Please provide a valid otp."
:invalid="v$.form.otp.$error"
/>
</CInputGroup>
<CInputGroup class="mb-3">
<CInputGroupText>
<CIcon icon="cil-lock-locked" />
</CInputGroupText>
<CFormInput
v-model.trim="form.password"
@input="setTouched('password')"
type="password"
placeholder="New password"
feedback="Please provide a password at least 6 characters"
:invalid="v$.form.password.$error"
/>
</CInputGroup>
<CInputGroup class="mb-3">
<CInputGroupText>
<CIcon icon="cil-lock-locked" />
</CInputGroupText>
<CFormInput
v-model.trim="form.confirmPassword"
@input="setTouched('confirmPassword')"
type="password"
placeholder="Re-enter new password"
feedback="Please repeat your password correctly."
:invalid="v$.form.confirmPassword.$error"
/>
</CInputGroup>
<div class="d-grid">
<SubmitButton
title="Submit"
:isSendingForm="isSendingForm"
/>
</div>
</CForm>
</CCardBody>
</CCard>
</CCol>
</CRow>
</CContainer>
</div>
</template>
That’s all for now thank you.