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)
}"
Related
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 need to pass props using Vue, I thought of JSON with object that includes name and value. I need to pass data to a different component but it changes as in each event the names and values change.
So for example I might have name: 'a' value: 'b', name: 'f' value: 'k' and in anorher event name: 'c' value: 'd'
my code that works but it work because i return hard coded data
data() {
return {
params: {
name:'bill',
value:'jones'
},
in child
#Component({
props:
{
urls: {
type: Object,
default: () => { return {name:'', value: ''} }
},
}
function with object params that i need to get the data from
getParams(url) {
paramsData[key] = value;
//console.log(key,value);
}
return params;
console.log(params)
You can use computed property names
emitEvent(name, value) {
let objectToEmit = {
[name]: value,
};
this.$emit("event-name", objectToEmit);
}
Now name and value will be set according to whatever you pass in emitEvent function.
You can read more about computed property names on below link
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
You can pretty much pass anything through and you can do it in many ways. Here are 4 examples:
Note: for all options we are assuming you have a parent component that is using the following syntax
<example-component :name="somename" :value="somevalue"></example-component>
Option1: props as a list of array of strings. The values can be anything in JS i.e. numbers, strings, objects, functions
<template>
<div>
<p v-text="example"></p>
</div>
</template>
<script>
export default {
name: "ExampleComponent",
prop: ['name','value']
}
</script>
Option 2: most common approach. Every prop to be a specific type of value. In these cases, you can list props as an object, where the properties’ names and values contain the prop names and types, respectively
<template>
<div>
<p v-text="example"></p>
</div>
</template>
<script>
export default {
name: "ExampleComponent",
props: {
name: {
type: String,
required: false,
default: 'something'
},
value: {
type: Number,
required: true
}
},
}
</script>
Option 3: you can validate or in this case pass in an Object and defaults returned from a factory function so you will always have some value. You can even return validation validator: function (value) {...}
<template>
<div>
<!-- some value from EventServiceClass-->
<p v-text="example.name"></p>
</div>
</template>
<script>
import EventServiceClass from "./EventServiceClass";
export default {
name: "ExampleComponent",
props: {
example: {
type: Object,
default: function () {
return {name:'a', value: 'b'}
}
},
},
}
</script>
Option 4: a little more advanced but in this example we are bind get and set to input properties on a form, which we would use to create factory form components
<template>
<div>
<input
id="name"
type="text"
v-model="name"
class="form--input light"/>
</div>
</template>
<script>
export default {
name: "ExampleComponent",
props: {
name: {
type: String,
default: ""
}
},
computed: {
name: {
get() {
return this.value;
},
set(value) {
this.$emit("input", value);
}
}
}
}
</script>
I have a computed property that I use as v-model on an input. I've written it this way to get reactivity -- this calls my setText Vuex action which I then can get with my getter text. It looks like this:
text: {
get() {
return this.text;
},
set(value) {
this.setText(value);
},
},
and I use it in my input like this:
<input class="input" type="text" v-model="text" />
This works well. Now, I've put the input in question into a separate component which I use. This means I have to pass the text v-model as props, which I do with :model.sync, like so:
<myInput :model.sync="text"/>
and in the myInput component I use the props like so:
<input class="input" id="search-order" type="text" :value="model" #input="$emit('update:model', $event)">
But this doesn't seem to work at all, whenever I type into the input, the input says: [object InputEvent] and if I try to see and the value of model it's {isTrusted: true}. I'm assuming it's because of the getters and setters I have on my computed property. How do I pass these down to the child component?
Instead of using the .sync modifier you can support the v-model directive in your custom component. v-model is syntax sugar for a value prop and an input event.
To support v-model just make sure your custom component has a value prop and emits an input event with the new value: this.$emit('input', event.target.value).
Here is an example of a <BaseInput> component I use, it's written in TypeScript:
<template>
<input
:type="type"
:value="value"
class="input"
v-on="listeners"
>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
name: 'BaseInput',
props: {
type: {
type: String,
default: 'text',
},
value: {
type: [String, Number],
default: '',
},
lazy: {
type: Boolean,
default: false,
},
number: {
type: Boolean,
default: false,
},
trim: {
type: Boolean,
default: false,
},
},
computed: {
modelEvent(): string {
return this.lazy ? 'change' : 'input'
},
parseModel(): (value: string) => string | number {
return (value: string) => {
if (this.type === 'number' || this.number) {
const res = Number.parseFloat(value)
// If the value cannot be parsed with parseFloat(),
// then the original value is returned.
return Number.isNaN(res) ? value : res
} else if (this.trim) {
return value.trim()
}
return value
}
},
listeners(): Record<string, Function | Function[]> {
return {
...this.$listeners,
[this.modelEvent]: (event: HTMLElementEvent<HTMLInputElement>) =>
this.$emit(this.modelEvent, this.parseModel(event.target.value)),
}
},
})
</script>
You can use it like so:
<BaseInput v-model="text" />
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
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">