v-model default value when it comes from Laravel - laravel

All inputs in the form are bind via v-model to the data in the Vue component like this
<validation-provider name="first_name" rules="required" v-slot="{ failed, errors }" :debounce="500">
<input type="text"
id="first_name"
name="first_name"
placeholder="First name"
class="form-control"
:class="{'is-invalid' : failed}"
:disabled="loading"
value="{{ auth()->user()->first_name }}"
tabindex="1"
v-model.trim="user.first_name"
>
<div class="invalid-feedback" v-show="failed">
#{{ errors[0] }}
</div>
v-model doesn't show a default value if there is one value="{{ auth()->user()->first_name }}" so I'm trying to solve this issue like this
data() {
return {
user: {
first_name: document.querySelector("input[name=first_name]").value,
},
loading: false
};
},
But I'm very sure this is not the best way to do this. I've tried looking for an answer, but they are kinda the same. What's the best way to do this?

you have to find a way pass that user information to (vue)js. your solution isnt wrong but in vue.js we work with data or state so my solution is create custom component for this problem like:
// blade
<UserForm :data='#json(auth()->user())' />
// component data
data() {
return {
user: this.data,
loading: false
// and what ever you need
};
},
of course there is a another way and you put user data in window DOM object throw head tag of your blade file. here:
#auth
<script>
window.user = #json(auth()->user())
</script>
#endauth
then you can access it in you components:
data() {
return {
user: {
first_name: window.user.first_name
},
loading: false
};
},

Related

Vue.js/Laravel: pass category id to Vue.js component

I'm using Vue.js with Laravel and facing a problem. I want to pass category id from the blade file to the Vue.js component as a prop. But don't know what is good practice and the right way for this.
I've defined the route something like this:
Route::view('/categories/{category}/edit', 'edit')->name('categories.edit');
and my edit.blade.php file is:
#extends('master')
#section('vue')
<div id="app">
<categories-edit :id=""></categories-edit>
</div>
#endsection
The Vue.js component code is:
<template>
<div class="container py-5">
<div class="row">
<div class="col-lg-12">
<div class="mb-3">
<label for="name" class="form-label">Name:</label>
<input type="text" v-model="formState.name" name="name" class="form-control" id="name" placeholder="Category Name" autocomplete="off">
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CategoriesEdit',
props: ['id'],
data: function () {
return {
formState: {
name: '',
photo: ''
}
}
},
mounted() {
},
methods: {
loadInitialData: function () {
const self = this;
axios.get(``).then(function (response) {
}).catch(function (err) {
});
}
}
}
</script>
When I'm entering the URL in the web browser. I'm getting this error.
http://example.test/categories/1/edit
Output:
Undefined variable $category
Since you are using Route::view() you do not have the traditional way of getting route parameters and pass them to the view. Luckily you can always get these on the request object and there is a request() helper that makes it easier for Blade views.
<categories-edit :id="{{ request()->route('category') }}"></categories-edit>

Get all values of custom input inside v-for vuejs

I created a vuejs custom input that I wanted to use to dynamically display inputs by using props within the custom input. I haven't shown them here because it would be too long.
By clicking on the submit button, which is also part of the custom input, I wanna be able to get the values of each input, but for some reason, I have only been able to get the value of the last input.
What am I doing wrong?
Custom input:
<template>
<div class="form-input">
<label :label="label" :for="name" v-if="label && type !='submit' ">{{label}} <span v-if="required">*</span></label>
<a v-if="multiple" href="#" class="btn">Upload</a>
<input v-model="inputVal" :multiple="multiple" v-if="type != 'textarea' && type != 'submit'" class="form-control" :required="required" :class="classes" :type="type" :name="name" :placeholder="placeHolder">
<textarea v-model="inputVal" :multiple="multiple" v-else-if="type != 'submit'" class="form-control" :required="required" :class="classes" :type="type" :name="name" :placeholder="placeHolder"></textarea>
<button :multiple="multiple" :name="name" v-else type="submit">{{label}}</button>
</div>
</template>
<script>
export default {
name: "Input",
data () {
return {
inputVal: null
}
},
watch: {
inputVal: {
handler: function(newValue, oldValue) {
this.$emit('input', newValue);
},
deep: true,
}
}
}
</script>
Form where custom input is used:
<template>
<div class="form container">
<form v-on:submit.prevent="sendMail" method="post" class="d-flex row shadow bg-dark border-right border-dark">
<h3 class="col-12">Contact me</h3>
<Input v-model="formInput" v-for="input in inputs" v-bind:key="input.name" :label="input.label" :multiple="input.multiple" :type="input.type" :name="input.name" :class="input.classes" :required="input.required"></Input>
</form>
</div>
</template>
<script>
import Input from "../components/Input";
export default {
name: "Contact",
components: {Input},
data() {
return {
formInput: null,
}
},
methods: {
sendMail () {
console.log(this.formInput);
}
}
}
</script>
The issue I see in your code is, you are using only one variable "formInput" ( in case of Contact component ) and "inputVal" ( in case of Input component ) but you have number of input fields from where you need data right.
The simplest way to deal with these kind of cases is to create a datastructure and loop through that.
For eg.
// Contact component ( i am making it simple to make you understand the scenario )
<template>
<div class="form container">
<form v-on:submit.prevent="sendMail" method="post" class="d-flex row shadow bg-dark border-right border-dark">
<h3 class="col-12">Contact me</h3>
<!-- we are looping through our data structure and binding each inputVal to this input -->
<input v-for="(input, i) in formInputs" :key="i" v-model="input.inputVal">
</form>
</div>
</template>
<script>
import Input from "../components/Input";
export default {
name: "Contact",
components: {Input},
data() {
return {
formInputs: [
{inputVal: ''},
{inputVal: ''},
{inputVal: ''},
],
}
},
methods: {
sendMail () {
// You can extract the data from formInputs as per your need
}
}
}
</script>

Displaying error messages in vuetify when validating nested object with vuelidate

I am using vuelidate to validate my form input and display the error messages using vuetifyjs. I managed to do the basic object validation and am able to show the error messages.
However I'm having issues with displaying the error messages when I validate a collection.
ISSUE
Example data structure:
contact: {
websites: [
{
url: 'http://www.something.com',
label: 'Website',
}
]
}
Example validation:
validations: {
websites: {
$each: {
url: {
url,
}
}
},
}
Example template:
<template v-for="(website, index) in websites">
<v-layout row :key="`website${index}`">
<v-flex xs12 sm9 class="pr-3">
<v-text-field
label="Website"
:value="website.url"
#input="$v.websites.$touch()"
#blur="$v.websites.$touch()"
:error-messages="websiteErrors"
></v-text-field>
</v-flex>
</v-layout>
</template>
Example computed error message:
websiteErrors() {
console.log('websites',this.$v.websites) // contains $each
const errors = []
if (!this.$v.websites.$dirty) {
return errors
}
// Issue is that all of them show must be valid, even if they are valid.
// Validation is basically broken.
// I also tried this.$v.websites.$each.url
!this.$v.websites.url && errors.push('Must be valid url')
return errors
},
Example method (Update, also tried method with passing index):
websiteErrors(index) {
console.log('this.$v.entity.websites', this.$v.entity.websites.$each.$iter, this.$v.entity.websites.$each.$iter[index], this.$v.entity.websites.minLength, this.$v.entity.websites.$each.$iter[index].url)
const errors = []
if (!this.$v.entity.websites.$dirty) {
return errors
}
!this.$v.entity.websites.$each.$iter[index].url && errors.push('Must be valid url')
return errors
},
However when I do this, it will always be true and therefore never show the error.
EXPECTED
I would like to have the same example working as seen in vuelidate sub-collection validation The difference is instead of looping in the template I would like to generate the message programmatically.
REFERENCE
Example provided by vuelidate:
import { required, minLength } from 'vuelidate/lib/validators'
export default {
data() {
return {
people: [
{
name: 'John'
},
{
name: ''
}
]
}
},
validations: {
people: {
required,
minLength: minLength(3),
$each: {
name: {
required,
minLength: minLength(2)
}
}
}
}
}
<div>
<div v-for="(v, index) in $v.people.$each.$iter">
<div class="form-group" :class="{ 'form-group--error': v.$error }">
<label class="form__label">Name for {{ index }}</label>
<input class="form__input" v-model.trim="v.name.$model"/>
</div>
<div class="error" v-if="!v.name.required">Name is required.</div>
<div class="error" v-if="!v.name.minLength">Name must have at least {{ v.name.$params.minLength.min }} letters.</div>
</div>
<div>
<button class="button" #click="people.push({name: ''})">Add</button>
<button class="button" #click="people.pop()">Remove</button>
</div>
<div class="form-group" :class="{ 'form-group--error': $v.people.$error }"></div>
<div class="error" v-if="!$v.people.minLength">List must have at least {{ $v.people.$params.minLength.min }} elements.</div>
<div class="error" v-else-if="!$v.people.required">List must not be empty.</div>
<div class="error" v-else-if="$v.people.$error">List is invalid.</div>
<button class="button" #click="$v.people.$touch">$touch</button>
<button class="button" #click="$v.people.$reset">$reset</button>
<tree-view :data="$v.people" :options="{rootObjectKey: '$v.people', maxDepth: 2}"></tree-view>
</div>
WHAT WENT WRONG
Shared computed property which causes the issue where all siblings share the same error message. (Solved by writing it inline)
Reactivity not triggered due to the array not being updated in a "reactive way" (Make sure to take note of Change Detection Caveats in this case instead of updating the index: I copy the array, replace item and then set the whole array.)
Wrong place to use vuelidate $each.$iter: Moved it from computed error message to v-for
SOLUTION
This is how to do it (Fixes 1 & 3):
<template v-for="(v, index) in $v.websites.$each.$iter">
<v-layout row :key="`website${index}`">
<v-flex xs12 sm9 class="pr-3">
<v-text-field
label="Website"
:value="v.$model.url"
#input="$v.websites.$touch()"
#blur="$v.websites.$touch()"
:error-messages="v.$dirty && !v.required ? ['This field is required'] : !v.url ? ['Must be a valid url'] : []"
/>
</v-flex>
</v-layout>
</template>
This is how my update method is now (Fixes 2):
updateWebsite(index, $event) {
const websites = [...this.websites];
websites[index] = $event;
this.updateVuex(`websites`, websites)
this.$v.websites.$touch()
},
Originally it was like this:
updateWebsite(index, $event) {
this.updateVuex(`websites[${index}]`, $event)
this.$v.websites.$touch()
},
ALTERNATIVE
There is another option, which is to wrap in this case website inside a component. That way you can keep the computed error message as it will not be shared.

Vue/Laravel How to pass form from blade to vue to use checkValidity

Made following button made by vue(single component).
<template>
<div>
<input type="submit" class="button_orange button_big" value="Register" v-show="!isSubmitting" #click="startSubmit">
<div class="button_disable button_big" v-show="isSubmitting">Register</div>
</div>
</template>
<script>
export default {
data() {
return {
isSubmitting: false,
};
},
methods: {
startSubmit() {
if (form.checkValidity()) {
this.isSubmitting = true;
}
},
reset() {
this.isSubmitting = false;
},
},
};
</script>
Current problem is how to pass the form to vue to use checkValidity() method. laravel blade is the following type source code.
if there is idea how to pass form from blade to vue, please help.
<form>
<input type="text" name="test">
<submit-button></submit-button>
</form>
What's the problem with this?
document.querySelector("form").checkValidity()
(Of course this would require to have this for as the only form on the page, so if you can, you should have an ID on the form and go for something like document.querySelector("#myForm01").checkValidity() instead.)

How to make Vue not replace my old value after the server side validation

I'm was using server side validation with laravel and fecthing the old value in case there was a validation error.
<input type="text" class="form-control" name="nombre" value="{{ old('nombre')}}">
But now I need to validate that input on the client with vue, cause it's required for other stuff.
<input type="text" class="form-control" name="nombre" value="{{ old('nombre')}}" v-model="vNombre" v-on:keyup="validarNombre">
The problem is that when the validation fails on the server side the old input value get's replaced with an empty string cause Vue is initializing that variable. Please help, thanks for reading.
<script type="text/javascript">
var avatar = new Vue({
el: '#validaciones',
data: {
vNombre: '',
validNombre: false,
},
methods: {
validarNombre: function(){
if (this.vNombre == '') {
this.validNombre = false
}
else {
this.validNombre = true;
}
}
}
})
</script>
Since vue is overwriting the field data you can use {{ old('nombre')}} in vNombre vue data field
data: {
vNombre: '{{ old("nombre")}}', //Be careful with single and double quotes
validNombre: false,
},

Resources