Looking for a way to translate(i18n) laravels form validation rules inside a Vue form - laravel

So I have build an form in laravel with Vue with some validation rules and this works, when there's an error it will show me an message but I also have an locales switcher present which also works for the text in the form but not for the validation output, so there are always in English. I am using the i18n
plugin to translate the text.
Is there a way to make the validation rules i18n ready?
registerController.php
protected function validator(array $data)
{
$customMessages = [
'name' => 'some custom message can go here...',
];
$validator = Validator::make($data, [
'name' => ['required', 'min:2', 'string', 'max:100'],
'email' => ['required', 'email', 'string', 'max:255', 'unique:users'],
'password' => [
'required'
'min:10',
'regex:/[a-z]/', // must contain at least one lowercase letter
'regex:/[A-Z]/', // must contain at least one uppercase letter
'regex:/[0-9]/', // must contain at least one digit
'regex:/[#$!%*#?&]/', // must contain a special character
],
], $customMessages);
return $validator;
}
componentForm.vue
<template>
<form #submit.prevent="formSubmit">
<el-input type="text" name="name" v-model="fields.name"
label="Name" :error="errors.name"/>
<el-input type="email" name="email" v-model="fields.email"
label="E-mailaddress" :error="errors.email"/>
<el-input type="password" name="password" v-model="fields.password"
label="Password" :error="errors.password"/>
<button type="submit" class="btn btn--primary btn--xl btn--block">
{{ $t("auth.register") }}
</button>
</form>
</template>
<script>
import espressoLocale from '../../Components/Contextual/Locale';
import espressoCard from '../../Components/UI/Card';
import elInput from '../../Components/Forms/Input';
export default {
components: {
elInput,
},
data() {
return {
fields:{
name: "",
email: "",
password: "",
},
errors: {
name: null,
email: null,
password: null,
},
}
},
methods: {
resetFields () {
this.fields.name = "";
this.fields.email = "";
this.fields.password = "";
},
formSubmit (e) {
e.preventDefault();
this.errors = {};
axios.get('/sanctum/csrf-cookie').then(response => {
axios({
method: 'post',
url: '/api/register',
data: {
name: this.fields.firstname,
email: this.fields.email,
password: this.fields.password,
},
validateStatus: (status) => {
return true;
}
}).then(response => {
if (response.data.success) {
} else {
this.errors = response.data.errors || {};
}
}).catch(function (error) { });
});
}
},
}
</script>

You should write :error="$t(errors.name)" in your components as you write {{ $t("auth.register") }} to show translated text about register. I assume that you get i18n locale structured object in your errors response, something like this:
errors: {
name: 'errors.name',
...
}

Related

Obtain values from Laravel Controller and display in Vue

In a Laravel 8 view, I have a Vue component with a form.
<contact-form-component contact-store-route="{{ route('contact.store') }}">
</contact-form-component>
ContactFormController.vue
<template>
<div>
<div v-if="success">
SUCCESS!
</div>
<div v-if="error">
ERROR!
</div>
<div v-show="!success">
<form
#submit.prevent="storeContact"
method="POST"
novalidate="novalidate"
#keydown="clearError"
>
<input type="text" name="fullname" v-model="formData.fullname" />
<input type="text" name="email" v-model="formData.email" />
<input type="text" name="phone" v-model="formData.phone"/>
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
import Form from "./Form.vue";
export default {
mixins: [Form],
props: {
contactStoreRoute: String,
},
data() {
return {
formData: {
fullname: null,
lname: null,
email: null,
phone: null,
message: null,
},
};
},
methods: {
storeContact() {
this.post(this.contactStoreRoute, this.formData);
},
},
mounted() {},
};
</script>
Form.vue
<template></template>
<script>
import FormErrors from "./FormErrors.vue";
export default {
name: "Form",
mixins: [FormErrors],
data() {
return {
success: false,
error: false,
errorMessage: "",
};
},
methods: {
post(url, data) {
this.success = false;
this.error = false;
axios
.post(url, data)
.then((res) => {
this.onSuccess(res.data.message);
})
.catch((error) => {
if (error.response.status == 422) {
this.setErrors(error.response.data.errors);
} else {
this.onFailure(error.response.data.message);
}
});
},
get(url, data) {
this.success = false;
this.error = false;
axios
.get(url, data)
.then((res) => {
this.onSuccess(res.data.message);
})
.catch((error) => {
if (error.response.status == 422) {
this.setErrors(error.response.data.errors);
} else {
this.onFailure(error.response.data.message);
}
});
},
onSuccess(message) {
this.reset();
this.success = true;
},
onFailure(message) {
this.error = true;
this.errorMessage = message;
},
reset() {
this.clearAllErrors();
for (let field in this.formData) {
this.formData[field] = null;
}
},
},
};
</script>
FormErrors.vue
<template></template>
<script>
export default {
name: "FormErrors",
data() {
return {
errors: {},
};
},
methods: {
setErrors(errors) {
this.errors = errors;
},
hasError(fieldName) {
return fieldName in this.errors;
},
getError(fieldName) {
return this.errors[fieldName][0];
},
clearError(event) {
Vue.delete(this.errors, event.target.name);
},
clearAllErrors() {
this.errors = {};
},
},
computed: {
hasAnyError() {
return Object.keys(this.errors).length > 0;
},
},
};
</script>
When the form is submitted, a laravel post route is called and the information is stored in the database.
Route::post('/contact/store', [ContactController::class,'store'])->name('contact.store');
After this, the Vue component now hides the form and displays a "success" message. So far, everything works great.
Now, I would like to add a step. Instead of success message, I want to obtain the last id entered in the db and show a new form with a hidden field last_id. I am unsure of how to obtain this information from the controller.
It would be a continuation of the previous form, but I do not want to gather all the data at once, I want it in steps. Now, it is also important to gather data from the first form, and if the user quits after the first form that is fine, no problem, but if the user continues with the second form I need to "link" it to the previous form through the last_id.
I think that I am not approaching this problem correctly, maybe I need to change the logic of what I am doing.
Adding last_id to ContactController return:
class ContactController extends Controller
{
public function store(Request $request)
{
$data = $request->validate([
'fullname' => 'required',
'email' => 'required|email',
'phone' => 'required',
]);
$contact_form = Contact::create($data);
$last_id = $contact_form->id;
return [
//how do I "send" this to the Vue component?
'last_id' => $last_id
];
}
}
This would be the "second step" form using the last ID. It would have it's own post route.
<h4>Your form has been successfully submitted, now please give us more info that will be linked to the previous form:</h4>
<form>
<input type="hidden" name="last_id" value="{HOW_TO_GET_THE_LAST_ID_HERE?}" />
<textarea name="message" required></textarea>
<input type="submit" value="Submit" />
</form>
You can use a JSON response:
https://laravel.com/docs/9.x/responses#json-responses
You can return the id in your Controller for example like this:
return response()
->json(['last_id' => $last_id])
You will be able to get the value from the response object of the post call
.then((res) => {
//get the id from the response
this.last_id = (res.data.last_id);
})

Vue 3 checkbox component does not work in laravel as checkbox (Accept terms) situation

I'm working on a laravel 9 project that uses vue 3. For each form input has been made field components.Everything works fine except registration checkbox (Accept terms). If the checkbox is used normally in vue, it works fine. as soon as a component is made of it, it no longer works. By clicking the chackbox on and off I get the message that the accept terms is required!
I hope you can help me with this
-Vue Registration Form componetnt
<template>
<form class="text-left" method="post" #submit.prevent="submit()">
<div class="alert alert-success mb-1" v-if="success" v-html="successMessage"></div>
<div class="alert alert-danger mb-1" v-if="errors.message" v-html="errors.message"></div>
<InputFieldWithoutIcon ref="email" type="email" name="email" label="E-mail" :errors="errors" #update:field="fields.email=$event"/>
<InputFieldWithoutIcon ref="password" type="password" name="password" label="Password" :errors="errors" #update:field="fields.password=$event"/>
<InputFieldWithoutIcon ref="password_confirmation" type="password" name="password_confirmation" label="Password Confirmation" :errors="errors" #update:field="fields.password_confirmation=$event"/>
<Checkbox :text="newsletter_text" name="newsletter" type="checkbox" :errors="errors" #update:field="fields.newsletter=$event"/>
<Checkbox :text="policy_text" name="policy_accepted" type="checkbox" :errors="errors" #update:field="fields.policy_accepted=$event"/>
<SubmitButtonWithoutIcon name="create account" type="submit" custom_btn_class="btn-block btn-primary" custom_div_class=""/>
</form>
</template>
<script>
import InputFieldWithoutIcon from "../components/InputFieldWithoutIcon";
import SubmitButtonWithoutIcon from "../components/SubmitButtonWithoutIcon";
import Checkbox from "../components/Checkbox";
export default {
name: "RegistrationUser",
components: {
InputFieldWithoutIcon,
SubmitButtonWithoutIcon,
Checkbox
},
data() {
return {
fields: {
email: '',
password: '',
password_confirmation: '',
policy_accepted: '',
newsletter: '',
},
errors: {},
success: false,
successMessage: '',
newsletter_text: 'I want to receive newsletters',
policy_text: 'I accept <a class="text-bold text-underline" href="" target="_blank" title="privacy policy">the policy</a>',
}
},
methods: {
submit() {
axios.post('/api/registration', this.fields)
.then(response => {
if (response.status === 200) {
this.$refs.email.value = null;
this.$refs.password.value = null;
this.$refs.password_confirmation.value = null;
this.fields = {
email: '',
password: '',
password_confirmation: '',
policy_accepted: '',
newsletter: '',
};
this.errors = {};
this.success = true;
this.successMessage = response.data.message;
}
}).catch(errors => {
if (errors.response.status === 422 || errors.response.status === 401) {
this.errors = errors.response.data.errors;
}
});
}
}
}
</script>
-Vue Checkbox component
<template>
<div class="custom-control custom-checkbox">
<input class="custom-control-input"
:id="name"
:name="name"
:type="type"
v-model="value"
#input="updateField()"
>
<label class="custom-control-label" :for="name">
{{ text }}
</label>
<div class="invalid-feedback" v-text="errorMessage()"></div>
</div>
</template>
<script>
export default {
name: "Checkbox",
props: [
'name',
'type',
'text',
'errors'
],
data: function () {
return {
value: false
}
},
computed: {
hasError: function () {
return this.errors && this.errors[this.name] && this.errors[this.name].length > 0;
}
},
methods: {
updateField: function () {
this.clearErrors(this.name);
this.$emit('update:field', this.value)
},
errorMessage: function () {
if (this.errors && this.errors[this.name]) {
return this.errors[this.name][0];
}
},
clearErrors: function () {
if (this.hasError) {
this.errors[this.name] = null;
}
}
}
}
</script>
-Laravel Registration request
public function rules(): array
{
return [
'email' => 'required|email|unique:users',
'password' => 'required|confirmed|min:8',
'newsletter' => 'nullable|boolean',
'policy_accepted' => 'accepted'
];
}
I've found the solution.
In the checkbox template instead of using #input="updateField()" I replaced that with #change="updateField()"
That's all!

Error when pass FormData from vue to laravel controller

i'm working on an laravel vuejs spa.
I try to update a post from the admin. I send the form datas on creating a new FromData who are sending with and axios request.
But the controller send me the error:
message: "The given data was invalid."
errors: {name: ["The name field is required."], description: ["The description field is required."]}
Here is the post/edit.vue
<template>
<div class="post-edit">
<form id="form" #submit.prevent="createPost">
<!-- Title -->
<div class="form-group">
<label for="title" class="form-label">Title</label>
<input id="title" v-model="post.title" :title="post.title" type="text"
name="title" class="form-control"
>
</div>
<!-- Category -->
<div class="form-group">
<label for="category" class="form-label">Category</label>
<select id="category" v-model="post.category" :value="post.category" :category="post.category"
name="category" class="form-control"
>
<option disabled :value="post.category">
Choisir une catégorie
</option>
<option v-for="(category) in categories" :key="category.id" :value="post.category">
{{ category.name }}
</option>
</select>
</div>
<div class="post-edit-image">
<img :src="path + post.image" alt="">
</div>
<!-- Image -->
<div class="form-group post-edit-select-image">
<label for="content" class="form-label">Image</label>
<input type="file" name="image" class="form-control" #change="selectedImage">
</div>
<!-- Content -->
<div class="form-group">
<label for="content" class="form-label">Content</label>
<editor
id="content"
v-model="post.content"
api-key="the-api-key"
:init="{
height: 500,
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount'
],
toolbar:
'undo redo | formatselect | bold italic backcolor | \
alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | removeformat | help'
}"
name="content"
:content="post.content"
/>
</div>
<!-- Submit Button -->
<button type="submit" class="btn btn-primary">
Sauvegarder
</button>
</form>
</div>
</template>
<script>
import axios from 'axios'
import { mapGetters } from 'vuex'
import Editor from '#tinymce/tinymce-vue'
export default {
middleware: 'auth',
components: {
'editor': Editor
},
props: ['id'],
data () {
return {
post: {
id: '',
user_id: '',
title: '',
category: '',
image: null,
content: ''
},
path: '/images/post/thumbnail/'
}
},
computed: {
...mapGetters({
user: 'auth/user',
categories: 'categories/categories'
})
},
beforeCreate () {
this.$store.dispatch('categories/fetchCategories')
},
created () {
this.getPostById(this.$route.params.id)
},
methods: {
selectedImage (event) {
this.post.image = event.target.files[0]
},
getPostById (id) {
axios.get('/api/posts/edit/' + id)
.then((response) => {
this.post.id = response.data.id
this.post.user_id = response.data.user_id
this.post.title = response.data.title
this.post.category = response.data.category
this.post.image = response.data.image
this.post.content = response.data.content
})
.catch((error) => {
console.log(error)
})
},
createPost (e) {
this.post.userId = this.user.id
let formData = new FormData(e.target)
let id = this.post.id
formData.append('title', this.post.title)
formData.append('category', this.post.category)
formData.append('image', this.post.image)
formData.append('content', this.post.content)
axios.patch('/api/posts/update/' + id, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then((response) => {
console.log(response)
this.$store.dispatch('posts/fetchPosts')
})
.catch((error) => {
console.log(error)
})
this.$router.push({ name: 'admin' })
}
}
}
</script>
And here the laravel controller PostController#update method
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(PostUpdateRequest $request, $id)
{
$post = Post::findOrFail($id);
$post->update($request->getValidRequest());
return response()->json($request, 200);
}
And the custom validator PostUpdateRequest.php
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'title' => 'required|max:250|unique:posts,title',
'category' => 'required',
'image' => 'mimes:jpg,jpeg,png,bmp',
'content' => 'required'
];
}
public function getValidRequest()
{
$image = $this->file('image');
$slug = Str::slug($this->input('title'));
if (isset($image)) {
$imageExt = $image->getClientOriginalExtension();
$currentDate = Carbon::now()->toDateString();
$imageName = $slug . '-' . $currentDate . '-' . uniqid() . '.' . $imageExt;
if (!Storage::disk('public')->exists('post')) {
Storage::disk('public')->makeDirectory('post');
}
if (!Storage::disk('public')->exists('post/thumbnail')) {
Storage::disk('public')->makeDirectory('post/thumbnail');
}
$path = "images/";
$postImage = Image::make($image)->resize(null, 1060, function ($constraint){
$constraint->aspectRatio();
})->save(public_path($path).'post/'.$imageName);
$thumbnail = Image::make($image)->fit(550, 550)
->save(public_path($path).'post/thumbnail/'.$imageName);
}
return [
'user_id' => Auth::user()->getAuthIdentifier(),
'category_id' => $this->input('category'),
'title' => $this->input('title'),
'slug' => $slug,
'content' => $this->input('content'),
'image' => $imageName,
'is_published' => $this->input('is_published') ?? false,
];
}
The api route in laravel
Route::patch('/update/{id}', 'Backend\PostController#update');
I use practically the same things to store a post, and it work's fine, the only difference is here i pass an id for the post to update and i not use the post method but patch for axios and the route (and i try with put, it's the same error).
thank's for your time !
append '_method' field with value 'PATCH' to formData
formData.append('_method', 'PATCH')
and send axios request post request to update data
axios.post('/api/posts/update/' + id, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})

Laravel + Vue Looping a Specific ID once

Hey guys so I'm planning to have a list of my recent chats. The problem is that i just want to loop the same Id (ex. Chatroom_id), in my case its looping the same the Chatroom Id 5 times, cause im checking a db that reciever_id is mine and the result is 5, hence the 5 times it loops the same Chatroom ID. I just want it to loop once ever chatroom Id
Laravel View // where i insert my vue component
<!--- Message or online followers and followings -->
<div id="messagesidebar" class="app-sidebar-userdetails">
<h6 class="sm text-white"> <i> Recent Chat </i> </h6>
<div id="recentchat">
<recentchat-component :user_id="{{ Auth::user()->id }}"></recentchat-component>
</div>
</div>
<!-- /# Message or online followers and followings -->
</div>
Vue Template
<div class="chats" v-for="recentchat in recentchats" v-bind:key="recentchat.id">
{{ recentchat.chatroom_id}}
</div>
Laravel Controller
public function fetchrecentchat($user_id)
{
$recentchat = Chat::Where('receiver_id', $user_id)
->orderBy('created_at', 'DESC')
->get();
return ChatResource::collection($recentchat);
}
Chat Resource
public function toArray($request)
{
// return parent::toArray($request);
return [
'id' => $this->id,
'message' => $this->message,
'chatroom_id' => $this->chatroom_id,
'user_id' => $this->user_id,
'receiver_id' => $this->receiver_id,
'created_at' => $this->created_at->diffForHumans(),
'updated_at' => $this->updated_at,
];
}
Tell me if you need to review codes that i did not add above so much thanks guys!
Update
Vue script
<script>
export default {
props: [ 'user_id' ],
data() {
return {
recentchats: [],
recentchat: {
id: '',
chatroom_id: '',
user_id: '',
receiver_id: '',
created_at: '',
updated_at: '',
}
}
},
created() {
this.fetchchatrooms();
},
methods: {
fetchchatrooms(){
fetch('/api/fetchrecentchat/' +this.user_id)
.then(res => res.json())
.then(res => {
console.log(res);
this.recentchats = res.data
});
}
},
mounted() {
console.log('Recent Chat mounted.')
}
}

I can not save values using POST method - laravel vue

I'm trying to save the data I send from the Event view. in the storeEvent method of the EventController driver but it gives me error 422 and I can not find the problem so far.
The Event model has a many-to-many relationship with the Categories model, and Event also has a many-to-many relationship with the Coins model, which is why I occupy vue-multiselect since the user can select several categories or several coins for the same event
Event.vue
<template>
<form v-on:submit.prevent="createdEvent" class="form-horizontal">
<div class="form-group row">
<label>Titulo</label>
<input type="text" name="title" maxlength="25" v-model="title">
</div>
<div class="form-group row">
<label>*Cryptodivisas</label>
<multiselect v-model="coinvalue" :options="coins"
:multiple="true" label="name"
track-by="id" placeholder="Seleccione">
</multiselect>
</div>
<div class="form-group row">
<label>*Categoría</label>
<multiselect v-model="categoryvalue" :options="categories"
:multiple="true" label="name"
track-by="id" placeholder="Seleccione">
</multiselect>
</div>
<div class="col-sm-12">
<button class="btn" type="submit">Crear evento</button>
</div>
</form>
<script>
import Multiselect from 'vue-multiselect';
export default {
components: {
Multiselect,
},
props: ['auth'],
data () {
return {
user: {},
title: '',
coins: [],
coinvalue: [],
categories: [],
categoryvalue: [],
}
},
created() {
this.getCoins();
this.getCategories();
},
mounted() {
this.user = JSON.parse(this.auth);
},
methods: {
getCoins(){
let urlCoin = '/dashboard/coins';
axios.get(urlCoin)
.then((response) => {
this.coins = response.data;
})
.catch((err) => {
})
},
getCategories(){
let urlCategories = '/dashboard/categories';
axios.get(urlCategories)
.then((response) => {
this.categories = response.data;
})
.catch((err) => {
})
},
createdEvent(){
let urlEvent = '/dashboard/newEvent';
const eventData = {
'id' : this.user.id,
'title' : this.title,
'coin' : this.coinvalue,
'category' : this.categoryvalue,
}
console.log(eventData);
axios.post(urlEvent, eventData)
.then((response) => {
console.log(ok);
})
.catch((err) => {
console.log(err.response.data);
})
}
</script>
storeEvent (EventController)
public function storeEvent(Request $request)
{
$this->validate($request, [
'title' => 'required|max:25',
'coin' => 'required',
'category' => 'required',
]);
$userAuth = Auth::user()->id;
$userEvent = $request->id;
if($userAuth === $userEvent){
$event = new Event;
$event->user_id = $userEvent;
$event->title = $request->title;
if($event->save()){
$event->coins()->attach($request->coin);
$event->categories()->attach($request->category);
return response()->json([
'status' => 'Muy bien!',
'msg' => 'Tu evento ya fue creado con éxito.',
], 200);
}
else {
return response()->json([
'status' => 'Error!',
'msg' => 'No pudimos crear tu evento.',
], 401);
}
}
}
I think the problem may be when I assign the values to the coin () -> attach () and category () -> attach () section, but I have no idea how to solve it due to my inexperience in the tool.
The system was made entirely in Laravel and it worked without problems, now that it is being updated with Vue it began to give inconveniences.
Any ideas? I occupy Laravel 5,6, Vuejs 2, Axios and Vue-multiselect 2
Try sending form data
Here is the example for you.
var urlEvent = '/dashboard/newEvent';
var form_data = new FormData();
form_data.append('id',this.user.id);
form_data.append('title',this.title);
for(let coin of this.coinvalue)
form_data.append('coin[]', coin);
for(let category of this.categoryvalue)
form_data.append('category[]', category);
axios(urlEvent,{
method: "post",
data: form_data
})
.then(res => {
console.log(ok);
})
.catch(err => {
console.log(err.response.data);
});
If this stills gives you a 422 status code (Unprocessable entities). Then try returning $request in you controller. And check what data are actually send to the controller and what your validation is.
422 means validation error so do a console.log or inspect the element on axios call and check that :
'title' : this.title,
'coin' : this.coinvalue,
'category' : this.categoryvalue,
Are not empty, cause right now some data from above is missing since its a 422 validation exception.

Resources