VeeValidate form submission fix
March 20, 2025 ยท AI Conversations
### **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? ๐
<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
### **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!