Vue.js Admin Panel Tutorial | Reset Password

Vue.js Admin Panel Tutorial | Reset Password

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.

Leave a Reply

Your email address will not be published. Required fields are marked *