Datepicker doesn't get validated on input - vue.js

I'm currently using the datepicker component in one of my projects. The component is supposed to throw an error message if I click in and out of the empty textfield of the datepicker menu. So far the error message only works if I enter a value and then remove it again.
There are already rules which check if the date-value of the textfield is longer than 0 Digits or not null.
HTML
<div id="app">
<v-app id="inspire">
<v-container grid-list-md>
<v-form v-model='validForm'>
<v-layout row wrap>
<v-flex xs12 lg6>
<v-text-field
v-model="text"
clearable
label="Regular Textfield"
:rules="rulesDatefield"
v-on="on"
></v-text-field>
<v-menu
v-model="menu1"
:close-on-content-click="false"
full-width
max-width="290"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model='date'
clearable
label="Datefield"
readonly
:rules="rulesDatefield"
v-on="on"
></v-text-field>
</template>
<v-date-picker
v-model="date"
#change="menu1 = false"
></v-date-picker>
</v-menu>
</v-flex>
</v-layout>
</v-form>
<v-btn :disabled="!validForm" #click='printValues()' color='primary'>Create</v-btn>
</v-container>
</v-app>
</div>
JS
new Vue({
el: '#app',
data: () => ({
validForm: false,
text: '',
date: '',
menu1: false,
rulesDatefield: [
v => String(v).length > 0 || 'Field is empty!',
v => v !== null || 'Field is empty!'
]
}),
methods: {
printValues: function() {
window.alert('Entered Text: ' + this.text + '\nEntered Date:' + this.date)
}
}
})
Codepen: https://codepen.io/anon/pen/XLLNZM?&editable=true&editors=101
I expect an error message from the date-textfield like in the regular textfield above if I enter and exit the datepicker without selecting a date.

<v-text-field
v-model="date"
clearable
readonly
label="Datefield"
:rules="rulesDatefield"
v-on="on"
#blur="date = date || null"
></v-text-field>
Seems strange but works, as you intended.

Related

Modify drop down value with preset value

I'm designing a form that can both used to create and edit something.
The form has a dropdown, which isn't initialized when created, but is set with a value when used for editing.
I want to be able to modify the value of the drop down, both for creating & updating, yet is only working when the mode is CREATE.
Here's my template:
<template>
<v-row justify="center">
<v-form ref="form" v-model="valid" lazy-validation>
<v-text-field v-model="title" label="Title*" :rules="rules.fieldRules" prepend-icon="mdi-text">
</v-text-field>
<v-text-field v-model="website_link" label="URL" prepend-icon="mdi-web">
</v-text-field>
<h3>Description</h3>
<text-editor #update:modelValue="getValue" :rules="rules.fieldRules" :modelValue="description">
</text-editor>
<br />
<v-select v-model="selectedState" :readonly="false" :items="items" filled label="Which state?*" prepend-icon="mdi-help"></v-select>
<v-file-input v-model="files" prepend-icon="mdi-camera" multiple label="File input"></v-file-input>
<v-layout v-for="file of files" :key="file">
<v-img :src="generateUrl(file)" max-width="10%" max-height="10%">
</v-img>
</v-layout>
<v-combobox v-model="tags" prepend-icon="mdi-lightbulb" label="Tags" chips clearable multiple filled
rounded></v-combobox>
<v-btn :disabled="mode == 'EDIT' ? false : !valid" color="success" class="mr-4" #click="validate">
Validate
</v-btn>
<v-btn color="blue" class="mr-4" #click="backToProfile">
Back
</v-btn>
</v-form>
</v-row>
</template>
selectedState is a computed method described as below here:
selectedState: {
get() {
if (this.mode == 'CREATE') {
return this.state
} else {
const state = this.items.filter((item) => item.value = this.currentConcept.state)[0].title;
this.setState(state);
return this.state;
}
},
set(value) {
console.log(`value: ${value}`)
this.state = value;
}
}
Am I missing something?
TIA

Can you please help me with my on step behind issue in my vue code?

I got a question about my vue code I'm making a filter dropdown but when I input a key to trigger the key down event for filtering the arr it's changing the dom each time after the second event (one step behind).
Here is the code pen :
https://codepen.io/dyonvangerwen/pen/zYvjMdY
it's only keeping the values in the arr that are matching the input
template:
<div id="app">
<v-app id="inspire">
<v-form>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="3">
<v-text-field
v-model="inputValue"
label="Filled"
placeholder="Placeholder"
filled
v-on:keydown="tester"
></v-text-field>
<v-card
class="mx-auto"
max-width="400"
tile
>
<v-list-ite >
<v-list-item-content v-for=" item in itemsInDropdown" :key="item">
<v-list-item-title>{{item}}</v-list-item-title>
</v-list-item-content>
</v-list-ite>
</v-card>
</v-col>
</v-row>
</v-container>
</v-form>
</v-app>
</div>
script:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
inputValue:'',
itemsInDropdown:['a','b','c','d','e','ab','cd','ea']
}),
methods:{
tester:function(){
this.itemsInDropdown = this.itemsInDropdown.filter((x)=>{
if(x.includes(this.inputValue)){
return true
}
else{return false}
})
}
}
})
It is better to use computed in this case:
Replace the method with this:
computed:{
itemsInDropdownFiltered:function(){
return this.itemsInDropdown.filter((x)=>{
return x.includes(this.inputValue);
});
}
}
Change the array to be rendered from itemsInDropdown to itemsInDropdownFiltered as follows:
<v-list-item-content v-for="item in itemsInDropdownFiltered" :key="item">

v-slot:badge and v-if does not work with a computed property

I'm working on a CMS project and I have an issue I can't figure out.
I Have a component where I'm showing IP's. On change I want a badge to appear, so the user knows "this field is changed".
But the thing is the badge won't show if I'm using "v-slot:badge".
In the v-if is a computed property, If I inspect the page with vue devtools ‘isStartIpValueChanged’ will be true on a change. So, it should work right?
Template
<v-list-item-content>
<v-form ref="form" v-model="valid">
<v-hover v-slot:default="{ hover }">
<v-row align-content="center" no-gutters>
<v-col>
<v-badge overlap color="red" right>
<template v-slot:badge v-if="isStartIpValueChanged">
<v-avatar color="red" size="6"></v-avatar>
</template>
<v-text-field
dense
:rules="apiIpRules"
v-model="apiIp.startIp"
#input="valueChanged()"
ref="startIp"
:class="hover ? 'hover-text-color' : ''"
placeholder="###.###.###.###">
</v-text-field>
</v-badge>
</v-col>
<v-col cols="1" class="text-center" align-self="center">
<p>-</p>
</v-col>
<v-col cols="1" class="text-center" align-self="center">
<v-btn v-show="hover" #click="deleteIp()" icon small color="red"><v-icon>mdi-minus-circle</v-icon></v-btn>
</v-col>
</v-row>
</v-hover>
</v-form>
Created and Computed (apiIp is a prop I get from the parent component)
created () {
this.apiIpsOriginalValueStartIp = this.apiIp.startIp
this.apiIpsOriginalValueEndIp = this.apiIp.endIp
this.apiIp.uuid = this.GenerateUUID()
},
computed: {
isStartIpValueChanged () {
return this.apiIp &&
(this.apiIp.startIp !== this.apiIpsOriginalValueStartIp ||
this.apiIp.uuid === null)
},
isEndIpValueChanged () {
return this.apiIp &&
(this.apiIp.endIp !== this.apiIpsOriginalValueEndIp ||
this.apiIp.uuid === null)
}
},
Anyone know what is going wrong here?
As according to Vuetify's own documentation, you should be using the v-model, directly on the v-badge, to show it only when you want it to.
<v-badge overlap color="red" right v-model="isStartIpValueChanged">
<template v-slot:badge>
<v-avatar color="red" size="6"></v-avatar>
</template>
<v-text-field
dense
:rules="apiIpRules"
v-model="apiIp.startIp"
#input="valueChanged()"
ref="startIp"
:class="hover ? 'hover-text-color' : ''"
placeholder="###.###.###.###">
</v-text-field>
</v-badge>
Doc: https://vuetifyjs.com/en/components/badges#show-on-hover

reset a vuetify stepper

I'm looking for a function who can resetting my stepper made with vuetify.
the e1 is set as 0 but if I make a function who reset this value to 0, it doesn't work and the stepper set as the same screen.
It is possible to reset a stepper to default state
Find the working codepen here: https://codepen.io/chansv/pen/wvvzddP?editors=1010
<div id="app">
<v-app id="inspire">
<v-stepper v-model="step" vertical>
<v-stepper-header>
<v-stepper-step step="1" :complete="step > 1">Your Information</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="2" :complete="step > 2">Your Address</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="3">Misc Info</v-stepper-step>
</v-stepper-header>
<v-stepper-items>
<v-stepper-content step="1">
<v-text-field label="Name" v-model="registration.name" required></v-text-field>
<v-text-field label="Email" v-model="registration.email" required></v-text-field>
<v-btn color="primary" #click.native="step = 2">Continue</v-btn>
</v-stepper-content>
<v-stepper-content step="2">
<v-text-field label="Street" v-model="registration.street" required></v-text-field>
<v-text-field label="City" v-model="registration.city" required></v-text-field>
<v-text-field label="State" v-model="registration.state" required></v-text-field>
<v-btn flat #click.native="step = 1">Previous</v-btn>
<v-btn color="primary" #click.native="step = 3">Continue</v-btn>
</v-stepper-content>
<v-stepper-content step="3">
<v-text-field label="Number of Tickets" type="number"
v-model="registration.numtickets" required></v-text-field>
<v-select label="Shirt Size" v-model="registration.shirtsize"
:items="sizes" required></v-select>
<v-btn flat #click.native="step = 2">Previous</v-btn>
<v-btn color="primary" #click.prevent="submit">Save</v-btn>
</v-stepper-content>
</v-stepper-items>
</v-stepper>
</v-app>
</div>
const defaultReg = Object.freeze({
name:null,
email:null,
street:null,
city:null,
state:null,
numtickets:0,
shirtsize:'XL'
});
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
step:1,
registration: Object.assign({}, defaultReg),
sizes:['S','M','L','XL']
}
},
methods:{
submit() {
this.registration = Object.assign({}, defaultReg);
this.step = 1;
}
}
})
A simpler approach at resetting your stepper is by using the key prop assigning to it a value and then in the function increasing this value. Something like this:
<template>
<v-stepper
:key="stepperKey"
v-model="e1"
>
...
</v-stepper>
</template>
<script>
export default {
data () {
return {
e1: 1,
stepperKey: 0
}
},
methods: {
increaseKey () { this.stepperKey++ }
}
}
</script>
The key prop or attribute is a build in Vue.js feature. Even if you don't see it it's been used on the back. Changing the key will trigger a re render.
If you have doubt about the key attribute/prop here is a nice article about it

Vue.js How to set scroll position back to top?

So whenever I click the "edit" button, a dialog pops up with a scrollbar where I can fill out information. But when I click "cancel" or "save" and then click that same "edit" button, the dialog pops up at the same scroll position. I would like to, every time when I click "edit" and the dialog opens up, be always at the top of the dialog page not where I left off last.
<template>
<!-- <div class="text-xs-center" v-if="storeState.admin" lazy> -->
<v-dialog
transition="dialog-bottom-transition"
scrollable
fullscreen
v-model="sheet"
v-if="storeState.admin"
lazy
persistent
>
<template v-slot:activator="{on}">
<v-btn flat color="green" dark icon v-on="on">
<v-icon>edit</v-icon>
</v-btn>
</template>
<div background-color="transparent" style="margin: auto auto 0 auto">
<v-card px-5 max-width="800px">
<v-card-title>
ADD SCHOLARSHIP
</v-card-title>
<v-form v-model="addDisabled" validation ref="editForm">
<v-container>
<v-layout wrap>
<v-flex
xs12
md4
>
<v-text-field
v-model="scholarship.title"
label="Scholarship name"
:counter="maxLength"
:rules="[maxLength_rules.max, minLength_rules.min]"
required
></v-text-field>
</v-flex>
<v-flex xs12 md4>
<v-text-field
v-model="scholarship.faculty"
label="Faculty"
:counter="maxLength"
:rules="[maxLength_rules.max, minLength_rules.min]"
required
></v-text-field>
</v-flex>
<v-flex xs12 md4>
<v-text-field
v-model="scholarship.dollarAmount"
label="Award amount"
required
:rules="[amount_rules.range, minLength_rules.min]"
></v-text-field>
</v-flex>
<v-flex ml-2 xs12 sm6 md4>
<v-menu
ref="menu2"
v-model="menu2"
:close-on-content-click="false"
:nudge-right="40"
:return-value.sync="availableDate"
transition="scale-transition"
offset-y
full-width
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="scholarship.available"
label="Date Available"
prepend-icon="event"
:rules="[minLength_rules.min]"
readonly
v-on="on"
></v-text-field>
</template>
<v-date-picker v-model="availableDate" no-title scrollable>
<v-spacer></v-spacer>
<v-btn flat color="primary" #click="menu2 = false">Cancel</v-btn>
<v-btn flat color="primary" #click="$refs.menu2.save(availableDate)">OK</v-btn>
</v-date-picker>
</v-menu>
</v-flex>
<v-flex ml-2 xs12 sm6 md4>
<v-menu
ref="menu1"
v-model="menu1"
:close-on-content-click="false"
:nudge-right="40"
:return-value.sync="dueDate"
transition="scale-transition"
offset-y
full-width
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="scholarship.deadline"
label="Due Date"
prepend-icon="event"
readonly
:rules="[minLength_rules.min]"
v-on="on"
></v-text-field>
</template>
<v-date-picker v-model="dueDate" no-title scrollable>
<v-spacer></v-spacer>
<v-btn flat color="primary" #click="menu1 = false">Cancel</v-btn>
<v-btn flat color="primary" #click="$refs.menu1.save(dueDate)">OK</v-btn>
</v-date-picker>
</v-menu>
</v-flex>
<v-flex xs12 md1>
<v-text-field
v-model="scholarship.requiredGpa"
label="Min GPA"
required
:rules="[gpa_rules.range, minLength_rules.min]"
></v-text-field>
</v-flex>
<v-flex xs12>
<v-textarea
outline
height="400"
v-model = "scholarship.description"
label="Scholarship Description"
:rules="[minLength_rules.min]"
></v-textarea>
</v-flex>
</v-layout>
</v-container>
</v-form>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn flat #click="sheet = false" >cancel</v-btn>
<v-btn color="primary" #click="updateFields(scholarship)" :disabled="!addDisabled" flat >SAVE</v-btn>
</v-card-actions>
</v-card>
</div>
</v-dialog>
<!-- </div> -->
</template>
<script>
import { store } from "../store.js";
import EDITSCHOLARSHIP from '../graphql/updateScholarship.gql'
export default {
data: () => ({
sheet: false,
valid: false,
dueDate: '',
availableDate: '',
storeState: store.state,
addDisabled: true,
menu1: '',
menu2: '',
gpa: '',
title: '',
faculty: '',
amount: '',
maxLength: 255,
description: '',
gpa_rules: {
range: v => v <= 4.00 && v >= 0.00 && v.length <=4 || 'GPA may only be within 0.00 - 4.00',
},
amount_rules: {
range: v => v <= 999999999.00 && v >= 0.00 || 'Amount must only contain numbers between 0.00 - 999999999.00',
},
maxLength_rules: {
max: v => v.length <= 255|| 'Max character length is 255',
},
minLength_rules: {
min: v => v.length > 0 || 'Required',
}
}),
props: {
scholarship: Object
},
methods: {
validate () {
if (this.$refs.editForm.validate()) {
this.addDisabled = false
}
},
editScholarship(scholarship) {
if (this.$refs.editForm.validate()) {
this.$apollo.mutate({
mutation: EDITSCHOLARSHIP,
variables: {
id: scholarship.id,
input: {
available: this.availableDate,
deadline: this.dueDate,
description: this.description,
dollarAmount: this.amount,
faculty: this.faculty,
requiredGpa: this.gpa,
title: scholarship.title,
visible: true,
}
}
}).then( (data) => {
this.$emit('showSnackbar', 'Scholarship successfully updated', 'success')
this.sheet = false
}).catch( (error) => {
this.$emit('showSnackbar', 'Scholarship update failed', 'error')
//this.text = error
//this.color = "error"
//this.snackbar = true
})
}
},
updateFields (scholarship) {
this.dueDate = scholarship.deadline
this.availableDate = scholarship.available
this.gpa = scholarship.requiredGpa
this.title = scholarship.title
this.faculty = scholarship.faculty
this.amount = scholarship.dollarAmount
this.description = scholarship.description
this.editScholarship(scholarship)
}
}
}
</script>
<style>
.scroll {
overflow-y: auto;
}
</style>
This is intended behavior. If the dialog box is not removed from the DOM when closed, its previous state will be retained unless otherwise modified.
You can choose one between three approaches I can think of in hindsight, 2 of which are what you are looking for.
Destroy the modal when not in use and re-instantiate when opening. A simple v-if toggling a boolean would do the trick, or a this.$destroy if your dialog box is a separate vue instance.
Add this.$el.scrollTop = 0 on your submit or cancel events. (A*)
Add scrollWrapper.scrollTop = 0 on your open dialog box method. (B*)
A: this.$el on item number 2 will only work if you are scrolling in the $el element, otherwise, you can access the target element using this.$el.querySelector('.scroll-wrapper')
B: Same as item number 2, but this uses vanilla JS references instead of relying on Vue, you should refer to your actual scroll wrapper.
Please add this code to the event to enable in your dialog.
this.$refs.editForm.$el.scrollIntoView({behavior: 'smooth'})