How to place v-text-field error message in vuetifyjs? - vue.js

I have v-text-field and I can't able to display the error message out of v-text-field dom position. in vuetify documentation there is not reference to this issue.
Is it possible to have the error message near the input?
Actual:
Expected:
<template>
<v-app>
<v-content>
<playground></playground>
<v-text-field
style="width:120px;"
class="numer"
:rules="[rules.required, rules.min, rules.max]"
v-model="numValue"
type="number"
append-outer-icon="add"
#click:append-outer="increment"
prepend-icon="remove"
#click:prepend="decrement"
></v-text-field>
{{numValue}}
</v-content>
</v-app>
</template>
<script>
import Playground from "./components/Playground";
export default {
name: "App",
components: {
Playground
},
data: function() {
return {
numValue: 0,
form: {
min: 2,
max: 10
},
rules: {
required: value => !!value || "Required.",
min: v => v >= this.form.min || `The Min is ${this.form.min}`,
max: v => v <= this.form.max || `The Max is ${this.form.max}`
}
};
},
methods: {
increment() {
if (this.numValue < this.form.max) {
this.numValue = parseInt(this.numValue, 10) + 1;
}
},
decrement() {
if (this.numValue > this.form.min) {
this.numValue = parseInt(this.numValue, 10) - 1;
}
}
}
};
</script>
<style>
.numer {
flex: 0 0 auto;
margin: 0;
padding: 0;
}
.numer input {
text-align: center;
}
</style>
The code in codesandbox

I know I'm late but if someone needs help:
Put this field inside a form.
<v-form ref="form">
<v-text-field
style="width:120px;"
class="numer"
:rules="[rules.required, rules.min, rules.max]"
v-model="numValue"
type="number"
append-outer-icon="add"
#click:append-outer="increment"
prepend-icon="remove"
#click:prepend="decrement"
></v-text-field>
</v-form>
Then call anywhere on template:
<div #click="$refs.form.validate()">Validate it!</div>
Or on the functions:
methods: {
foo() {
this.$refs.form.validate()
}
}

Related

How to know which rule is failing in vuetify's v-text-field?

As explained How to know if rule is failing in vuetify's v-text-field?, I can see any rule is failing or not with the model value of <v-form>.
In addition, I'd like to know which rule is falling with multi-rule v-text-area.
For example, with the following component,
<template>
<div>
<v-form v-model="valid">
<v-text-field
v-model="model"
type="number"
label="Fetch Frequency"
:rules="[rules.loanMin, rules.required]"
min="100"
suffix="seconds"
></v-text-field>
</v-form>
</div>
</template>
<script>
export default {
data: function(){
return {
device: "",
model: "",
item: "",
rules: {
required: v => !!v || 'Required.',
loanMin: v => (v && v >= 100) || 'minimum interval is 100 min',
}
}
}
}
</script>
Are there any way to know which rule ("required" or "loanMin") is falling?
You will need to distinguish based on the error messages inside the errorBucket:
<template>
<div>
<v-form v-model="valid">
<v-text-field
ref="text"
v-model="model"
type="number"
label="Fetch Frequency"
:rules="[rules.loanMin, rules.required]"
min="100"
suffix="seconds"
></v-text-field>
</v-form>
</div>
</template>
<script>
export default
{
data()
{
return {
textRef: null,
device: "",
model: "",
item: "",
rules:
{
required: v => !!v || 'Required.',
loanMin: v => (v && v >= 100) || 'minimum interval is 100 min',
}
}
},
computed:
{
failedValidation()
{
if (!this.textRef) return '';
switch (this.textRef.errorBucket[0])
{
case 'Required.': return 'required';
case 'minimum interval is 100 min': return 'loanMin';
default: return '';
}
},
},
mounted()
{
this.textRef = this.$refs.text;
},
}
</script>

How to validate only onblur with Vuelidate?

From my parent component I'm calling my custom input child component this way:
<custom-input
v-model="$v.form.userName.$model"
:v="$v.form.userName"
type="text"
/>
And here's my custom input component:
<template>
<input
v-bind="$attrs"
:value="value"
v-on="inputListeners"
:class="{ error: v && v.$error }"
>
</template>
<script>
export default {
inheritAttrs: false,
props: {
value: {
type: String,
default: ''
},
v: {
type: Object,
default: null
}
},
computed: {
inputListeners () {
const vm = this
return Object.assign({},
this.$listeners,
{
input (event) {
vm.$emit('blur', event.target.value)
}
}
)
}
}
}
</script>
This triggers validation errors from the very first character entered in the input field (which is arguably poor UX, so I really don't understand why this is default behavior).
Anyway, how to trigger such errors only on blur event?
This is not default behavior - it's your code!
Vuelidate validates (and raise errors) only after field is marked as dirty by calling $touch method. But when you are using $model property ($v.form.userName.$model) for v-model, it calls $touch automatically - docs
So either do not use $model for binding and call $touch by yourself on blur event (or whenever you want)
Alternatively you can try to use .lazy modifier on v-model but that is supported only on native input elements (support for custom components is long time request)
Example below shows how to implement it yourself....
Vue.use(window.vuelidate.default)
Vue.component('custom-input', {
template: `
<input
v-bind="$attrs"
:value="value"
v-on="inputListeners"
:class="status(v)"
></input>
`,
inheritAttrs: false,
props: {
value: {
type: String,
default: ''
},
v: {
type: Object,
default: null
},
lazy: {
type: Boolean,
default: false
}
},
computed: {
inputListeners() {
const listeners = { ...this.$listeners }
const vm = this
const eventName = this.lazy ? 'change' : 'input'
delete listeners.input
listeners[eventName] = function(event) {
vm.$emit('input', event.target.value)
}
return listeners
}
},
methods: {
status(validation) {
return {
error: validation.$error,
dirty: validation.$dirty
}
}
}
})
const { required, minLength } = window.validators
new Vue({
el: "#app",
data: {
userName: ''
},
validations: {
userName: {
required,
minLength: minLength(5)
}
}
})
input {
border: 1px solid silver;
border-radius: 4px;
background: white;
padding: 5px 10px;
}
.dirty {
border-color: #5A5;
background: #EFE;
}
.dirty:focus {
outline-color: #8E8;
}
.error {
border-color: red;
background: #FDD;
}
.error:focus {
outline-color: #F99;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuelidate/dist/vuelidate.min.js"></script>
<script src="https://unpkg.com/vuelidate/dist/validators.min.js"></script>
<div id="app">
<custom-input v-model="$v.userName.$model" :v="$v.userName" type="text" lazy></custom-input>
<pre>Model: {{ userName }}</pre>
<pre>{{ $v }}</pre>
</div>
Try to emit the input event from the handler of blur event so :
instead of :
v-on="inputListeners"
set
#blur="$emit('input', $event.target.value)"

Invalid prop: type check failed error in VueJS

I am trying to use a component named CardRenderer.vue which renders card using array of Objects. I am using the same component again & again to render the data. I am having this error "[Vue warn]: Invalid prop: type check failed for prop "renderData" when I tried passing prop from component.
I tried passing different values and different types but it did'nt work.
Here is the code:
CardRenderer.vue
<template lang="html">
<div>
<b-container class="bv-example-row">
<b-row v-for="(row, i) of rows" v-bind:key="i">
<b-col v-for="(item, j) of row" v-bind:key="j" >
<!-- you card -->
<b-card
:title="item.title"
img-src="item.icon"
img-alt="Image"
img-top
tag="article"
style="max-width: 20rem;"
class="mb-2"
>
<b-card-text>
<h1>{{item.name}}</h1>
<pre>{{item.description}}</pre>
</b-card-text>
<b-button :href="'/dashboard/'+item.name" variant="primary">More</b-button>
</b-card>
</b-col>
</b-row>
</b-container>
</div>
</template>
<script lang="js">
export default {
name: 'CardRenderer',
props: {
renderData: []
},
data() {
return {
rows: null
}
},
mounted() {
const itemsPerRow = 3
let rowss = []
let arr = this.renderData
// eslint-disable-next-line
// console.log(this.renderData)
for (let i = 0; i < arr.length; i += itemsPerRow){
let row = []
for (let z = 0; z < itemsPerRow; z++) {
row.push(arr[z])
}
rowss.push(row)
}
this.rows = rowss
// eslint-disable-next-line
console.log(this.rows)
},
methods: {
},
computed: {
// rows() {
// }
}
}
</script>
<style scoped>
</style>
CardGrouper.vue:
<template lang="html">
<div class = "full" >
<div class="h-50" style=" background-color: #C8544F">
<h1 align="center">{{$store.getters.responseAPI.title}} </h1>
<CardRenderer :renderData=this.$store.getters.responseAPI.apps />
</div>
</div>
</template>
<script>
import CardRenderer from "./CardRenderer.vue"
/* eslint-disable */
export default {
name: 'CardGrouper',
components: {
CardRenderer
},
props: [],
mounted() {
},
data() {
return {
}
},
methods: {
},
computed: {
}
}
</script>
<style scoped >
.full{
width: 100vw;
height: 90vh;
background: linear-gradient(to bottom, Red 30%, white 50%);
}
</style>
Something.vue
<template lang="html">
<!-- <h1>Something</h1> -->
<CardRenderer :renderData=valObj />
</template>
<script lang="js">
import CardRenderer from './CardRenderer'
export default {
name: 'something',
components: {
CardRenderer
},
props: [],
data() {
return {
valObj: []
}
},
mounted() {
let key = this.findUrl()
let value = this.$store.getters.responseAPI.apps.filter((elem) => {
if(elem.name == key) return elem.apps
})
if (value.length > 0)
this.valObj = value[0].apps
//eslint-disable-next-line
console.log(this.valObj)
},
methods: {
findUrl() {
let url = window.location.pathname.split("/").slice(-1)[0];
return url
}
},
computed: {
}
}
</script>
<style scoped >
.something {
}
</style>
I am having this error.
It looks like this.
This is the data I am passing as a prop from Something.vue
This is how value looks like
Error is being generated somewhere from Something.vue.
I am passing array of objects as a prop.
How do i rectify this error, to make it work.
Set the renderData type as Array and default value to []:
props: {
renderData: {
type: Array,
deafult: () => []
}
}
It appears that you are defining your renderData prop as an array [] but probably are passing an object to it or something.
Either simplify it and do...
props: ['renderData']
Or if you are passing an object to it do..
props: {
renderData: {
type: Object,
}
}
If it is an array of objects do..
props: {
renderData: {
type: Array,
default: () => [{}];
}
just for doc.
// Object with a default value
propE: {
type: Object,
// Object or array defaults must be returned from
// a factory function
default: function () {
return { message: 'hello' }
}
},
This is in vue prop documentation

Passing Array as prop not received on the other component

I am trying to pass an array of objects as a prop to a component. The Array is being passed without an array. I am neither receiving any compilation error.
I tried actually looking on to the object tried some stuff. But it did not work
Here is the code:
CardRenderer.vue:
<template lang="html">
<div>
<b-container class="bv-example-row">
<b-row v-for="(row, i) of rows" v-bind:key="i">
<b-col v-for="(item, j) of row" v-bind:key="j" >
<!-- you card -->
<b-card
:title="item.title"
img-src="item.icon"
img-alt="Image"
img-top
tag="article"
style="max-width: 20rem;"
class="mb-2"
>
<b-card-text>
<h1>{{item.name}}</h1>
<pre>{{item.description}}</pre>
</b-card-text>
<b-button :href="'/dashboard/'+item.name" variant="primary">More</b-button>
</b-card>
</b-col>
</b-row>
</b-container>
</div>
</template>
<script lang="js">
export default {
name: 'CardRenderer',
props: {
renderData: {
type: Array,
required: true,
default: () => ([]),
}
},
data() {
return {
rows: null
}
},
mounted() {
const itemsPerRow = 3
let rowss = []
// eslint-disable-next-line
console.log(this.renderData)
let arr = this.renderData
for (let i = 0; i < arr.length; i += itemsPerRow) {
let row = []
for (let z = 0; z < itemsPerRow; z++) {
row.push(arr[z])
}
rowss.push(row)
}
this.rows = rowss
// eslint-disable-next-line
// console.log(this.rows)
},
methods: {
},
computed: {
// rows() {
// }
}
}
</script>
<style scoped>
</style>
Something.vue
<template lang="html">
<!-- <h1>Something</h1> -->
<CardRenderer :renderData=valObj />
</template>
<script lang="js">
import CardRenderer from './CardRenderer'
export default {
name: 'something',
components: {
CardRenderer
},
props: [],
data() {
return {
valObj: []
}
},
mounted() {
let key = this.findUrl()
let value = this.$store.getters.responseAPI.apps.filter((elem) => {
if(elem.name == key) return elem.apps
})
if (value && value.length > 0)
this.valObj = value[0].apps
//eslint-disable-next-line
console.log(this.valObj)
},
methods: {
findUrl() {
let url = window.location.pathname.split("/").slice(-1)[0];
return url
}
},
computed: {
}
}
</script>
<style scoped >
.something {
}
</style>
This is what i am sending as a prop.
This is what i receive on the component
There's a couple of issues here.
First, you should be using kebab-cased attribute names and quotes around the value...
<CardRenderer :render-data="valObj" />
The second issue is timing related. In your component, you calculate rows based on the initial renderData in the mounted hook but this will not update when the parent component alters valObj.
What you should do instead is use a computed property which will react to valObj / renderData changes.
For example
data () { return {} }, // removed rows from data
computed: {
rows () {
let itemsPerRow = 3
let rows = []
for (let i = 0; i < this.renderData.length; i += itemsPerRow) {
rows.push(this.renderData.slice(i, i + itemsPerRow))
}
return rows
}
}

Repeated data in vue

i`m having a problem with my vue, the problem is im trying to print 2 words, that is 'A.2' and 'B.3', but when im printing it, it just show 'B.3' and 'B.3'. here is my code
this is a simple quiz project, so everytime a user choose option a with true status it should be adding 1 point to the score, i haven`t made that yet.
<template>
<div class="hello">
<h1 v-if="show">hai</h1>
<h1 v-else>hehe</h1>
<p>{{ nama}}</p>
<input type="text" v-model="nama">
<button type="button" v-on:click="hideTitle">Click Me</button>
<h3> 1.Yang Dipakai Di sepatu adalah </h3>
<p>{{ nama}}</p>
<h3 v-for="j in jawaban">
<input type="radio">
{{j}}
</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data : function() {
return{
nama: 'Luthfi',
show: true
},
{
jawaban: 'A.2',
correct: true
},
{
jawaban: 'B.3',
correct: false
},
{
jawaban: 'C.4',
correct: false
}
},
methods: {
hideTitle() {
this.show = !this.show
}
},
mounted: function () {
this.nama = 'Fitra'
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
i expect there is 4 output from option A to D, but it kept showing me same option
In your code, data() returns only one object that contains
{
nama: 'Luthfi',
show: true
}
You must change this like:
data : function() {
return{
nama: 'Luthfi',
show: true,
jawaban: 'A.22',
correct: true,
jawabann: 'B.3'
}
}