I have 2 fields to validate but always returns true in the method validateBeforeSubmit() after i submit.
Whitout button submit it works nice and returns errors.first(name) in the good language
What do i do wrong?
App.vue:
<form #submit.prevent="validateBeforeSubmit">
<form-input v-on:input="handleTitle" :validate="'required|email'" label="email" labelvalue="email" type="text" placeholder="" name="email" :value="title" classname="form-control" id=""></form-input>
<form-input v-on:input="handleLink" :validate="'required'" label="Link" labelvalue="Link" type="text" placeholder="" name="link" :value="link" classname="form-control" id=""></form-input>
methods: {
validateBeforeSubmit() {
this.$validator.validateAll().then((result) => {
if (result) {
// eslint-disable-next-line
alert('Form Submitted!');
return;
}
alert('Correct them errors!');
});
},
Input.vue:
<template>
<div>
<div class="form-group">
<span>{{ errors.first(name) }}</span>
<label v-if="label" :for="label" v-html="labelvalue"></label>
<input v-validate="validate" v-on:input="updateValue($event)" :type="type" :placeholder="placeholder" :name="name" :value="value" :class="classname" :id="id">
</div>
</div>
</template>
export default {
props: {
validate: String,
type: String,
placeholder: String,
name: String,
value: String,
classname: String,
id: String,
label: String,
labelvalue: String
},
methods: {
updateValue: function (evt) {
this.$emit('input', evt)
}
}
}
validateAll does not look into the child component. You have to inject parent validator. Add inject: ['$validator'] in Input.vue file. It should solve the problem. The export block will look like this
export default {
inject: ['$validator'],
props: {
validate: String,
type: String,
placeholder: String,
name: String,
value: String,
classname: String,
id: String,
label: String,
labelvalue: String
},
methods: {
updateValue: function (evt) {
this.$emit('input', evt)
}
}
}
For more informatin about inject you can look into this reference
Related
So I have encountered a weird issue with my code, that I hope to get some help with.
I have a custom "Input" component where I have a normal HTML input with some styling. I have then called this component with a value and a function to call upon changes. No v-model is used as I have to do some validation on the field. However, it doesn't work. I can see that the value variable in the "Input" component changes correctly, but it does not impact the HTML input element at all, if you enter multiple values into the input field. How can this be?
InputComponent
<template>
<label class="block text-sm flex justify-end lg:justify-start w-28 h-10">
<span class="text-gray-800">{{ label }}</span>
<input
class="block text-black placeholder:text-black placeholder:opacity-40 w-14 lg:w-full rounded-lg text-center"
:placeholder="placeholder"
:type="type"
:value="value"
#input="handleInput($event.target.value)"
/>
</label>
</template>
<script>
export default {
props: {
label: String,
placeholder: String,
type: String,
value: String,
size: String,
},
methods: {
handleInput(value) {
this.$emit('input', value);
}
},
}
</script>
Page component calling Input
<template>
<Input
type="number"
placeholder="0"
size="sm"
:value="test"
#input="changePlannedVacationDay($event)"
/>
</template>
<script>
export default {
data() {
return {
test: ""
};
},
methods: {
changePlannedVacationDay(value) {
let localValue = value;
const maxValue = 5;
if (parseInt(localValue) < 0) {
localValue = "0";
} else if (parseInt(localValue) > maxValue) {
localValue = maxValue.toString();
}
this.test = localValue;
}
},
</script>
You should use a computed property with getter and setter:
<template>
<input v-model="localModel">
</template>
<script>
export default
{
name: 'CustomInputComponent',
props:
{
value:
{
type: String,
default: null,
},
},
computed:
{
localModel:
{
get()
{
return this.value;
},
set(val)
{
this.$emit('input', val);
}
},
},
}
</script>
In the parent component you should use a watcher to detect value changes and act upon them.
How can I get all input values from dynamic form as a single object with key-values pairs? Where key is input name and value is user input.
I have a custom input component. In app component I dynamically render inputs from array:
<template>
<div>
<form #submit.prevent="submit">
<ul>
<li v-for="input in inputFields" :key="input.label">
<base-input
:placeholder="input.placeholder"
:type="input.type"
:name="input.name"
v-model="userInput[input.name]"
></base-input>
</li>
</ul>
<button type="submit">Console.log input</button>
</form>
</div>
</template>
<script>
import BaseInput from "./components/BaseInput.vue";
export default {
components: { BaseInput },
data() {
return {
inputFields: INPUT_FIELDS,
userInput: {
name: "",
phone: "",
email: "",
location: "",
address: "",
comment: "",
},
};
},
methods: {
submit() {
console.log("userInput:", this.userInput);
},
},
};
</script>
Then in base-input component:
<template>
<input
:type="type"
:name="name"
:placeholder="placeholder"
#input="$emit('update:modelValue', $event.target.value)"
:value="modelValue[name]"
/>
</template>
<script>
export default {
emits: ["update:modelValue"],
props: {
label: {
type: String,
required: false,
},
placeholder: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
name: {
type: String,
required: false,
},
modelValue: {},
},
};
</script>
Expected behavior: click on submit --> get object like in console:
{
name: "user name",
phone: "1111",
email: "test#test.com",
location: "World",
address: "",
comment: "no",
},
Actual behavior: I can only type 1 letter in a field and no more.
Here's a codesandbox: https://codesandbox.io/s/optimistic-alex-dzw0mp?file=/src/App.vue
You were close. You need to remove [name] from the end of :value="modelValue[name]" in the base-input component. Since you are already scoping to name in the parent component it is wrong to also look for a [name] prop on the value of modelValue within your child component.
See it working here: https://codesandbox.io/s/unruffled-cohen-bivefc?file=/src/components/BaseInput.vue
Your component template should be:
<input
:type="type"
:name="name"
:placeholder="placeholder"
#input="$emit('update:modelValue', $event.target.value)"
:value="modelValue"
/>
I have 2 formfields Title and Link, when typing a text into Title also link gets the same value.
Something goes wrong with updateValue method, this wil execute twice.
How can i achieve that both fields getting there own values?
App.vue
<form-input v-on:input="handleTitle" :label="'title'" :labelvalue="'Titel'" :type="'text'" :placeholder="''" :name="'title'" :value="title" :classname="'form-control'" :id="''"></form-input>
<form-input v-on:input="handleLink" :label="'Link'" :labelvalue="'Link'" :type="'text'" :placeholder="''" :name="'link'" :value="title" :classname="'form-control'" :id="''"></form-input>
data() {
return {
title: '',
link: '',
}
}
methods: {
handleTitle: function (evt) {
this.title = evt.target.value
},
handleLink: function (evt) {
this.link = evt.target.value
},
Template:
<input v-on:input="updateValue($event)" :type="type" :placeholder="placeholder" :name="name" :value="value" :class="classname" :id="id">
export default {
props: {
type: String,
placeholder: String,
name: String,
value: String,
classname: String,
id: String,
label: String,
labelvalue: String
},
methods: {
updateValue: function (evt) {
console.log(this.$emit('input', evt))
this.$emit('input', evt)
}
}
}
Your prop :value have the same variable in both <form-input :value="title"> change one to <form-input :value="link">
Ik have the following input.vue template, everything works fine, but how can i implement/add a class if after input some text and and validate has no errors?
<input
v-validate="validate"
v-on:input="updateValue($event)"
:type="type"
:placeholder="placeholder"
:name="name" :value="value"
:class="{'form-control':'form-control','errorinput': errors.has(name)}"
:id="id">
export default {
inject: ['$validator'],
props: {
validate: String,
type: String,
placeholder: String,
name: String,
value: String,
classname: String,
id: String,
label: String,
labelvalue: String
},
methods: {
updateValue: function (evt) {
console.log(this.errors);
this.$emit('input', evt)
}
}
}
after validate, the field would have a dirty flag. You can get a dirty flag by doing $validator.fields.find({ name: name }).flags.dirty.
This is an example of how you can add a class after validated while without error, by combining dirty and errors.has
:class="{
'form-control':'form-control',
'errorinput': errors.has(name),
'noerrorinput': $validator.fields.find({ name: name })
&& $validator.fields.find({ name: name }).flags.dirty
&& !errors.has(name)
}"
for me NuxtJS application i've got multiple components thats come together in one form. Here the code:
AppButton.vue
<template>
<button
class="button"
:class="btnStyle"
:disabled="disabled"
v-bind="$attrs"
v-on="$listeners"><slot /></button>
</template>
<script>
export default {
name: 'AppButton',
props: {
btnStyle: {
type: String,
default: ''
},
disabled: {
type: String
}
}
}
</script>
AppFormInput.vue
<template>
<div class="form-input">
<input
v-bind="$attrs"
:name="name"
:value="value"
:type="type"
:placeholder="placeholder"
:max="max"
:min="min"
:pattern="pattern"
:required="required"
:disabled="disabled"
#input="$emit('input', $event.target.value)">
</div>
</template>
<script>
export default {
name: 'AppFormInput',
props: {
controlType: {
type: String,
default: 'input'
},
name: {
type: String
},
value: {
type: String,
default: ''
},
type: {
type: String,
default: ''
},
placeholder: {
type: String,
default: ''
},
max: {
type: String
},
min: {
type: String
},
pattern: {
type: String
},
required: {
type: String,
},
disabled: {
type: String
}
}
}
</script>
FormGroup.vue
<template lang="html">
<div class="form-group">
<AppLabel :label="label"/>
<AppFormInput :v-bind="$attrs"/>
</div>
</template>
<script>
import AppLabel from '~/components/atoms/AppLabel'
import AppFormInput from '~/components/atoms/AppFormInput'
export default {
components: {
AppLabel,
AppFormInput
},
props: {
label: {
type: String,
default: 'Form Label'
}
}
}
</script>
Form.vue
<template lang="html">
<form #submit.prevent="onSave">
<FormGroup label="Form Input" v-model="formPosts.forminput"/>
<FormGroup label="Form Input Disabled" disabled="disabled" v-model="formPosts.forminputdisabled"/>
<FormGroup label="Form Input With Placeholder" placeholder="Set placeholder" v-model="formPosts.forminputplaceholder"/>
<FormGroup label="Form Input Required" required="required" v-model="formPosts.forminputrequired"/>
<FormGroup label="Form Email" type="email" v-model="formPosts.forminputemail"/>
<FormGroup label="Form Date" type="date" v-model="formPosts.forminputdate"/>
<FormGroup label="Form Number" type="number" value="1" min="1" max="5" v-model="formPosts.forminputnumber"/>
<FormGroup label="Form Tel" type="tel" pattern="\d{3}-\d{3}-\d{4}" placeholder="XXX-XXXX-XXXX" v-model="formPosts.forminputtel"/>
<!--Add Select normal and disabled-->
<FormGroup label="Form Radio" type="radio" value="1" v-model="formPosts.forminputradio"/>
<FormGroup label="Form Checkbox" type="checkbox" value="2" v-model="formPosts.forminputcheckbox"/>
<AppButton type="submit" btn-style="btn-brand">Save</AppButton>
</form>
</template>
<script>
import FormGroup from '~/components/molecules/FormGroup'
import AppButton from '~/components/atoms/AppButton'
export default {
components: {
FormGroup,
AppButton
},
data() {
return{
formPosts: {
forminput: '',
forminputdisabled: '',
forminputplaceholder: '',
forminputrequired: '',
forminputemail: '',
forminputdate: '',
forminputnumber: '',
forminputtel: '',
forminputradio: '',
forminputcheckbox: '',
}
}
},
methods: {
onSave() {
console.log(this.formPosts);
this.$emit('submit', this.formPosts)
},
}
}
</script>
At least, the index.vue
<template lang="html">
<div class="container">
<h1>Forms</h1>
<Form #submit="onSubmitted"/>
</div>
</template>
<script>
import Form from '~/components/organism/Form'
export default {
components: {
Form
},
methods: {
onSubmitted(data){
console.log(data);
}
}
}
</script>
when the form is submitted the fields stays empty. The required field must have a value for example but it stays empty. I think that the some components not have access through the value of the field. Does anyone have tips?
Thanx for any help
$attrs (in v-bind="$attrs") will only bind attributes, not props (bold is mine):
vm.$attrs: Contains parent-scope attribute bindings (except for class and style) that are not recognized (and extracted) as props. When a component doesn't have any declared props, this essentially contains all parent-scope bindings (except for class and style), and can be passed down to an inner component via v-bind="$attrs" - useful when creating higher-order components.
You need to set the props yourself in FormGroup.vue.
In FormGroup.vue, declare the value prop so you can use in the template:
<script>
import AppLabel from '~/components/atoms/AppLabel'
import AppFormInput from '~/components/atoms/AppFormInput'
export default {
components: {
AppLabel,
AppFormInput
},
props: {
label: {
type: String,
default: 'Form Label'
},
value: { // added this
type: String // added this
} // added this
}
}
</script>
In FormGroup.vue, Add :value="value" #input="$emit('input', $event)" to the template.
<template lang="html">
<div class="form-group">
<AppLabel :label="label"/>
<AppFormInput :v-bind="$attrs" :value="value" #input="$emit('input', $event)" />
</div>
</template>
The code above will set <AppFormInput>'s value and will propagate (up) it's input event. Not the v-models in Form.vue should work.