VeeValidate form submission fix

User
Vuetify form login laravel vue external
Assistant
To create an external Vuetify-based login form for a Laravel and Vue.js application, follow these steps:

### **1. Backend – Laravel Setup**
Ensure that Laravel handles authentication correctly with a token-based approach like Sanctum or JWT.

#### **Install Laravel Sanctum (if not installed)**

composer require laravel/sanctum
php artisan vendor:publish --provider="LaravelSanctumSanctumServiceProvider"
php artisan migrate

Add `HasApiTokens` to `User.php` model:

use LaravelSanctumHasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

In `config/cors.php`, allow your frontend domain:

'allowed_origins' => ['http://localhost:5173'], // Adjust for your setup

In `app/Http/Kernel.php`, add `LaravelSanctumHttpMiddlewareEnsureFrontendRequestsAreStateful::class` to `$middlewareGroups['api']`.

#### **Define Login API Route**
Add a login route in `routes/api.php`:

use IlluminateHttpRequest;
use IlluminateSupportFacadesAuth;

Route::post('/login', function (Request $request) {
    $credentials = $request->validate([
        'email' => 'required|email',
        'password' => 'required'
    ]);

    if (Auth::attempt($credentials)) {
        $user = Auth::user();
        $token = $user->createToken('authToken')->plainTextToken;
        return response()->json(['token' => $token, 'user' => $user]);
    }

    return response()->json(['message' => 'Invalid credentials'], 401);
});

### **2. Frontend – Vue 3 + Vuetify**
#### **Install Vuetify**

yarn add vuetify@next axios vue-router

#### **Create Login Page**
Create `Login.vue` inside `src/views/`:

<script setup>
import { ref } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';

const router = useRouter();
const email = ref('');
const password = ref('');
const loading = ref(false);
const errorMessage = ref('');

const login = async () => {
    loading.value = true;
    errorMessage.value = '';

    try {
        const response = await axios.post('http://localhost:8000/api/login', {
            email: email.value,
            password: password.value
        });

        localStorage.setItem('token', response.data.token);
        router.push('/dashboard'); // Redirect after login
    } catch (error) {
        errorMessage.value = error.response?.data?.message || 'Login failed';
    } finally {
        loading.value = false;
    }
};
</script>

<template>
  <v-container class="fill-height">
    <v-row align="center" justify="center">
      <v-col cols="12" sm="8" md="4">
        <v-card class="pa-5">
          <v-card-title class="text-center text-h5">Login</v-card-title>
          <v-card-text>
            <v-alert v-if="errorMessage" type="error" dense>{{ errorMessage }}</v-alert>
            <v-form @submit.prevent="login">
              <v-text-field label="Email" v-model="email" type="email" required></v-text-field>
              <v-text-field label="Password" v-model="password" type="password" required></v-text-field>
              <v-btn :loading="loading" color="primary" block type="submit">Login</v-btn>
            </v-form>
          </v-card-text>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

### **3. Router Setup**
Modify `router/index.js`:

import { createRouter, createWebHistory } from 'vue-router';
import Login from '@/views/Login.vue';
import Dashboard from '@/views/Dashboard.vue';

const routes = [
  { path: '/', component: Login },
  { path: '/dashboard', component: Dashboard, meta: { requiresAuth: true } }
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

// Navigation Guard
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token');
  if (to.meta.requiresAuth && !token) {
    next('/');
  } else {
    next();
  }
});

export default router;

### **4. Axios Global Setup**
Set up Axios to automatically send the token:

import axios from 'axios';

axios.defaults.baseURL = 'http://localhost:8000/api';
axios.defaults.headers.common['Authorization'] = `Bearer ${localStorage.getItem('token')}`;

### **5. Protect Laravel Routes**
To protect API routes, wrap them in `auth:sanctum` middleware in `routes/api.php`:

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

### **Final Notes**
– Make sure Laravel API is running: `php artisan serve`
– Start Vue frontend: `yarn dev`
– Ensure Laravel's CORS settings allow requests from your frontend
– Adjust API URLs based on your environment

Would you like to integrate logout or register functionality as well? ๐Ÿš€

User
<script>
import { ref,inject,onMounted } from "vue";
import Google from '@/assets/images/auth/social-google.svg';
import { useAuthStore } from '@/stores/auth';
import { useLoadingStore } from '@/stores/loading';
import { useRouter } from 'vue-router';
import { Form } from 'vee-validate';
import publicApi from '@/publicApi';
const apiUrl = inject('apiUrl');
const loadingStore = useLoadingStore();
const authStore = useAuthStore();
const router = useRouter();
const alertMessage=ref("");
const alertColor=ref("");
const checkbox = ref(false);
const valid = ref(false);
const show1 = ref(false);
//const logform = ref();
const password = ref('');
const username = ref('');
const passwordRules = ref([
  (v: string) => !!v || 'Password is required',
  (v: string) => (v && v.length <= 10) || 'Password must be less than 10 characters'
]);
const emailRules = ref([(v: string) => !!v || 'E-mail is required', (v: string) => /.+@.+..+/.test(v) || 'E-mail must be valid']);

/* eslint-disable @typescript-eslint/no-explicit-any */
const submit = async () => {
    loadingStore.setLoading(true);
    alertMessage.value = "";
    alertColor.value = "";
     try {
        const response = await publicApi.post(`${apiUrl}/login`, {
            email: username.value,  // Use values from VeeValidate
            password: password.value
        });

        console.log(response);
        if (response.data.success) {
            alertMessage.value = "Login successful!";
            alertColor.value = 'success';
            authStore.setUser(response.data.user, response.data.token);
            router.push('/dashboard');
        } else {
            alertMessage.value = response.data.message;
            alertColor.value = 'error';
            console.error("Login failed:", response.data.message);
        }
    } catch (error: any) {
        alertMessage.value = error.response?.data.message;
        alertColor.value = 'error';
        console.error("Error during login:", error.response?.data.message || error.message);
    } finally {
        loadingStore.setLoading(false);
    }
};
</script>

Sign in with Google

OR

Sign in with Email address

Forgot password?

Sign In

Don't Have an account?

<style>
.custom-devider {
  border-color: rgba(0, 0, 0, 0.08) !important;
}
.googleBtn {
  border-color: rgba(0, 0, 0, 0.08);
  margin: 30px 0 20px 0;
}
.outlinedInput .v-field {
  border: 1px solid rgba(0, 0, 0, 0.08);
  box-shadow: none;
}
.orbtn {
  padding: 2px 40px;
  border-color: rgba(0, 0, 0, 0.08);
  margin: 20px 15px;
}
.pwdInput {
  position: relative;
  .v-input__append {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
  }
}
.loginForm {
  .v-text-field .v-field--active input {
    font-weight: 500;
  }
}
</style>

this give error when click on sign in
chunk-U3LI7FBV.js?v=63b73135:12141 Uncaught (in promise) TypeError: e.preventDefault is not a function
at prevent (chunk-U3LI7FBV.js?v=63b73135:12141:21)
at cache..cache. (chunk-U3LI7FBV.js?v=63b73135:12158:20)
at vee-validate.js?v=63b73135:2246:20
prevent @ chunk-U3LI7FBV.js?v=63b73135:12141
cache..cache. @ chunk-U3LI7FBV.js?v=63b73135:12158
(anonymous) @ vee-validate.js?v=63b73135:2246
Promise.then
submissionHandler @ vee-validate.js?v=63b73135:2238
callWithErrorHandling @ chunk-U3LI7FBV.js?v=63b73135:2263
callWithAsyncErrorHandling @ chunk-U3LI7FBV.js?v=63b73135:2270
invoker @ chunk-U3LI7FBV.js?v=63b73135:11202Understand this errorAI

Assistant
The error message `TypeError: e.preventDefault is not a function` suggests that the `@submit.prevent="submit"` event inside your “ component from `vee-validate` is not correctly handling the submission event.

### **Possible Causes & Fix**
1. **Ensure `Form` is correctly used from VeeValidate**
The `vee-validate` “ component does not pass the standard event object like a native HTML form would. Instead, it passes `values` and a `context` object to the handler.

**Change the `submit` function to accept values instead of `event`:**

const submit = async (values: any) => {  // Change here
       loadingStore.setLoading(true);
       alertMessage.value = "";
       alertColor.value = "";
       try {
           const response = await publicApi.post(`${apiUrl}/login`, {
               email: values.username,  // Fetch from `values`
               password: values.password
           });

           console.log(response);
           if (response.data.success) {
               alertMessage.value = "Login successful!";
               alertColor.value = 'success';
               authStore.setUser(response.data.user, response.data.token);
               router.push('/dashboard');
           } else {
               alertMessage.value = response.data.message;
               alertColor.value = 'error';
               console.error("Login failed:", response.data.message);
           }
       } catch (error: any) {
           alertMessage.value = error.response?.data.message;
           alertColor.value = 'error';
           console.error("Error during login:", error.response?.data.message || error.message);
       } finally {
           loadingStore.setLoading(false);
       }
   };

2. **Update the “ component to use the correct binding**
Modify the `vee-validate` “ component to pass form values:

<Form @submit="submit" v-slot="{ values }" class="mt-7 loginForm">
     <v-text-field
       v-model="values.username"
       :rules="emailRules"
       label="Email Address / Username"
       class="mt-4 mb-8"
       required
       density="comfortable"
       hide-details="auto"
       variant="outlined"
       color="primary"
     ></v-text-field>
     <v-text-field
       v-model="values.password"
       :rules="passwordRules"
       label="Password"
       required
       density="comfortable"
       variant="outlined"
       color="primary"
       hide-details="auto"
       :append-icon="show1 ? '$eye' : '$eyeOff'"
       :type="show1 ? 'text' : 'password'"
       @click:append="show1 = !show1"
       class="pwdInput"
     ></v-text-field>

     <v-btn color="secondary" block class="mt-2" variant="flat" size="large" type="submit">
       Sign In
     </v-btn>
   </Form>

### **Key Fixes**
โœ… Use `values.username` and `values.password` instead of `username.value` and `password.value`.
โœ… Remove `@submit.prevent` and replace it with `@submit="submit"` inside “.
โœ… Use `v-slot="{ values }"` to access form fields properly inside “.

Now your login form should work without throwing the `e.preventDefault is not a function` error. ๐Ÿš€ Let me know if you need further clarification!

Leave a Comment

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