Watch date input value and use it with v-model - vuejs2

I tried for 4 days to get a stupid date from the input, that the user selected. Looks probably simple but believe me that I ask here because I run out of solutions.
I have a page/component AddNewEvent and inside this input where the user adds the date and time. I have to get it in my v-model so I can send it back to database.
I use bootstrap 4 input type="datetime-local" to get the date and time. I tried to use some vue plugins for date but all are based on bootstrap 3 and in the project is bootstrap 4.
Inside template:
<div class="form-group">
<label class="eda-form-label" for="event-name">Event Name <span>(Max characters number is 60)</span></label>
<input type="text" class="eda-form-input" v-model="newEvent.eventName">
</div>
<div class="form-group">
<label class="eda-form-label" for="exampleTextarea">Event Description <span>(Max characters number is 2000)</span></label>
<textarea class="eda-form-input" rows="3" v-model="newEvent.description"></textarea>
</div>
<div class="form-group">
<div class="row">
<div class="col-6 ">
<label class="eda-form-label" for="start-time">START TIME</label>
<input class="form-control" type="datetime-local" v-model="dateNow">
</div>{{createdDate(value)}}
<div class="col-6">
<label class="eda-form-label" for="end-time">END TIME</label>
<input class="form-control" type="datetime-local" v-model="dateEnd">
</div>
</div>
</div>
In the script:
data() {
return {
dateNow: '',
value: '',
oldValue: ''
}
},
watch: {
dateNow(val, oldVal) {
this.value = val;
this.oldValue = oldVal;
}
},
methods: {
createEvent(){
axios.post("/event", this.newEvent,
{'headers':{'X-AUTH-TOKEN': localStorage.token}},
{'headers':{'Content-Type': 'application/json'}})
.then((response) => {
alertify.success("Success! You added a new the user");
this.$router.push('/events');
})
.catch((response) => {
alertify.error();
})
},
}
If I use in the input, how is right now, v-model="dateNow" works. I can see when I select date, time am/pm shows me the seleted date. But I have to use it like this
v-model="newEvent.dateNow"
v-model="newEvent.dateEnd"
so I can add it to newEvent and send the whole obj back to database.
createdDate is a function that transforms the date in a real date. It's only for testing, because I have to send the date in ms back to database.
Someone please show me what I do wrong, because I'm not so very advance in vuejs.

First you need to initialize dateNow varable in datetime-locale:
data() {
return {
dateNow: new Date().toLocaleString(),
value: '',
oldValue: ''
}
},
It support this format: "2017-09-18T08:30".
I think this will solve your problem.
And also you need to check Browser compatibility, as it is not supported in IE, Firefox and Safari
or you can try:
<input type="datetime-local" :value="dateValue" #input="updateValue($event.target.value)" >
in script:
data() {
return {
dateValue: new Date().toLocaleString()
};
},
methods: {
updateValue: function(value) {
this.dateValue= value;
this.$emit('input', value);
}
}
Try this

Related

How do I display a value as a decimal place in vuejs input field?

I'm trying to display a 2000.00 in an input field in vuejs but it strips the .00.
<div id="app">
<input
class="form-control"
type="number"
v-model="solicitorsFees"/>
</div>
new Vue({
el: "#app",
data: {
solicitorsFees: 2000.00,
},
})
How do I get the input field to display 2000.00?
jsfiddle
I can do this with a calculated property. But I need to apply this to multiple input fields.
solicitorsFeesDecimal: function(){
return(this.solicitorsFees.toFixed(2))
},
<input class="form-control" type="number" v-model="solicitorsFeesDecimal"/>
Solution:
<input class="form-control" type="number" :value="(this.solicitorsFees).toFixed(2)"/>
The appropriate solution is to use a computed property with a custom getter and setter, as well as using the v-model.number modifier:
Template
<div id="app">
<input
class="form-control"
type="number"
v-model.number="solicitorsFeesDisplay"/>
</div>
Script
new Vue({
el: "#app",
computed: {
solicitorsFeesDisplay: {
get: function() {
return this.solicitorsFees.toFixed(2)
},
set: function(newValue) {
this.solicitorsFees = newValue
}
}
},
data() {
return {
solicitorsFees: 2000.00
}
},
})
See a working example on CodeSandbox.
you should you step in input field:
<input type="number" v-model="solicitorsFees" step="0.01">
This solves the problem:
<input class="form-control" type="number" :value="(this.solicitorsFees).toFixed(2)"/>
You can simply use parseFloat and toFixed together to define the number of decimal places you want.
v-model= parseFloat(solicitorsFees).toFixed(2)
Also, another suggestion is to make the data object as a function, as in your fiddle you are using an object.
data () {
return {
solicitorsFees: 2000.00
}
}

Using vuelidate on element inside an object array

I am trying to validate a form with vuelidate where it it shows the same amount of field depending on how many users i want to add, so if i want to add 3 users than 3 Name and age fields will show up.
However when i try to use #blur to validate the fields i always get an "undefined" error even thouygh i already set up may validations corectly. What exacly am i doing wrong?
<div v-for="(user, index) in users" :key="index" >
<div>
<Input
label="name"
v-model="user.name"
#blur="$v.user.name.$touch()"
/>
</div>
<div >
<Input
label="Date"
mask="##/##/####"
v-model="user.birth"
#blur="$v.user.birth.$touch()"
/>
</div>
</div>
data() {
return {
users: [
{
name: '',
birth: '',
}
],
}
validations: {
benefs: {
$each: {
name: {
required
},
birth: {
required
}
}
},
Did you import the library on your page?
You can try to use
#blur="$v.user.name.required"

v-if not working for form validation and displaying errors

I'm trying to validate a simple form that contains two fields:
A select box
A file field
If one of the fields aren't filled in, a div (containing an error label) should be rendered next to the corresponding input field.
The problem: My 'error divs' aren't rendered when pushing data to the errors object (if the form is invalid).
Please note my console.log statement, that tells me that my error object has a key 'file' and a key 'selectedSupplier'.
Side note: I'm following this example: https://v2.vuejs.org/v2/cookbook/form-validation.html
Differences are, that I'd like to show error labels next to the corresponding field and that I'm setting errors in my errors object, instead of a simple array. So what could I be doing wrong?
Thanks.
This is my Main.vue file:
<template>
<div>
<form #submit="upload">
<div class="mb-8">
<h1 class="mb-3 text-90 font-normal text-2xl">Import Order Csv</h1>
<div class="card">
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="supplier_id" class="inline-block text-80 pt-2 leading-tight">Supplier</label>
</div>
<div class="py-6 px-8 w-1/2">
<select v-model="selectedSupplier" id="supplier_id" name="supplier_id" ref="supplier_id" class="w-full form-control form-input form-input-bordered">
<option v-for="supplier in suppliers" v-bind:value="supplier.id">{{ supplier.name }}</option>
</select>
<div v-if="errors.hasOwnProperty('selectedSupplier')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="csv_file" class="inline-block text-80 pt-2 leading-tight">File</label>
</div>
<div class="py-6 px-8 w-1/2">
<input id="csv_file" type="file" name="file" ref="file" #change="handleFile">
<div v-if="errors.hasOwnProperty('file')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
</div>
</div>
<div class="flex items-center">
<button type="submit" class="btn btn-default btn-primary inline-flex items-center relative">Import</button>
</div>
</form>
</div>
</template>
<script>
export default {
mounted() {
this.listSuppliers();
},
data() {
return {
errors: [],
file: '',
suppliers: [],
};
},
methods: {
checkForm() {
if (!this.selectedSupplier) {
this.errors.selectedSupplier = 'Supplier required';
}
if (!this.file) {
this.errors.file = 'File required';
}
},
listSuppliers() {
const self = this;
Nova.request()
.get('/tool/import-order-csv/suppliers')
.then(function (response) {
self.suppliers = response.data.data;
})
.catch(function (e) {
self.$toasted.show(e, {type: "error"});
});
},
handleFile: function (event) {
this.file = this.$refs.file.files[0];
},
upload: function (event) {
this.checkForm();
if (this.errors.hasOwnProperty('selectedSupplier') || this.errors.hasOwnProperty('file')) {
console.log(this.errors); // this actually shows both errors!
event.preventDefault();
}
const formData = new FormData();
formData.append('file', this.file);
formData.append('supplier_id', this.$refs.supplier_id.value);
const self = this;
Nova.request()
.post('/tool/import-order-csv/upload',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function (response) {
self.$toasted.show(response.data.message, {type: "success"});
})
.catch(function (e) {
self.$toasted.show(e.response.data.message, {type: "error"});
});
}
}
}
</script>
Apparently I had to use v-show instead of v-if, because v-if would be 'lazy' and will not render my error-div when the errors var gets filled.
It's working now, but not 100% sure if this is the best way, as I found another tutorial where v-if is used for form validation.(https://medium.com/#mscherrenberg/laravel-5-6-vue-js-simple-form-submission-using-components-92b6d5fd4434)
I was getting the same error, this is how I solved the problem,
<div v-if="errors.field1.length > 0 ? true : false"> // true or false
If you fix the code like this it will work
The reason might because the way you reassign object is not reactive, which not trigger v-if to re-calculate
this.errors.selectedSupplier = 'Supplier required';
this.errors.file = 'File required';
If you still want to use v-if , try change to this approach
this.errors = {...this.errors, selectedSupplier: 'Supplier required' }
this.errors = {...this.errors, file: 'File required' }
The way I handle my errors with VueJS is through lists and their length attribute.
I have an errors object in my data that looks like this:
errors: {
field1: [],
field2: [],
}
Then, when I submit the form, I will:
Empty all the lists for the errors (ie clearing the previous errors)
.push() new errors in the right lists (and .push() makes the Vue reactive)
Finally, in my form, my respective errors divs are displayed based on the length of the list:
<div class="error" v-if="errors.field1.length > 0">
use a v-for to display all the errors from the list
</div>
Hope it helps

Vee-Validate custom date validation

I was wondering if there is anyway you can write a custom date validation using vee-validate plugin where the end date cannot be less than the start date? I have looked high and low, and there is nowhere I can find a definite answer to this.
If there is no way to implement this, then I can make do without it, however, right now what I have implemented in my template for my start date is:
<input type="text" id="startDate" name="startDate" class="form-control" v-model="startDate" v-validate="'required|date_format:DD-MM-YYYY'" :class="{'input': true, 'is-danger': errors.has('startDate') }">
<label class="mb-0" for="startDate">Start Date</label>
<span v-show="errors.has('startdate')" class="text-danger"><center>{{ errors.first('startdate') }}</center></span>
My script looks like this:
export default {
name: 'App',
data: () => ({
task: '',
startDate: '',
startTime: '',
endDate: '',
endTime: '',
description: 'test'
}),
methods: {
validateBeforeSubmit() {
this.$validator.validateAll().then((result) => {
if (result) {
// eslint-disable-next-line
alert('Form Submitted!');
return;
}
alert('Correct them errors!');
});
}
}
};
But there is no validation that is showing up. I think I am missing something in my script but I am not sure how to implement the date into there. Any help would be greatly appreciated.
First, it's maybe some typo error but in your template you use startDate and startdate lowercased.
Then, to answer your question, it's possible to define a custom validator with a date comparison with vee-validate.
As your chosen date format "DD-MM-YYYY" is not a valid javascript Date format, the string dates need to be rewritten into a valid format to make it work.
Vue.use(VeeValidate)
new Vue({
el: "#app",
data() {
return {
startDate: '',
endDate: '',
}
},
created() {
let self = this
this.$validator.extend('earlier', {
getMessage(field, val) {
return 'must be earlier than startDate'
},
validate(value, field) {
let startParts = self.startDate.split('-')
let endParts = value.split('-')
let start = new Date(startParts[2], startParts[1] -1, startParts[0]) // month is 0-based
let end = new Date(endParts[2], endParts[1] -1, endParts[0])
return end > start
}
})
},
methods: {
validateBeforeSubmit() {
this.$validator.validateAll().then((result) => {
if (result) {
alert('Form Submitted!');
return;
}
alert('Correct them errors!');
});
}
}
})
.is-danger, .text-danger {
color: red;
}
<script src="https://unpkg.com/vee-validate#2.0.0-rc.19/dist/vee-validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<div>
<input type="text" name="startDate" v-model="startDate"v-validate="'required|date_format:DD-MM-YYYY'" :class="{'input': true, 'is-danger': errors.has('startDate') }">
<label class="mb-0" for="startDate">Start Date</label>
<span v-show="errors.has('startDate')" class="text-danger">{{ errors.first('startDate') }}</span>
</div>
<div>
<input type="text" name="endDate" v-model="endDate" v-validate="'required|date_format:DD-MM-YYYY|earlier'" :class="{'input': true, 'is-danger': errors.has('endDate') }">
<label class="mb-0" for="endDate">End Date</label>
<span v-show="errors.has('endDate')" class="text-danger">{{ errors.first('endDate') }}</span>
</div>
<button #click="validateBeforeSubmit">Save</button>
</div>
Note: i put the custom validator inside the created hook for the example but you can put it inside any file you want in your projet. Just import it correctly as the documentation recommends.

VueJS Date Range

I have to select two dates to set input date range. I have used this library but not date range input, have used single datepicker for each dates (Start Date and End Date) as requirement is to display only one calendar at a time. I have used vuejs in my project. So I have to bind those input values to the model. I'm new in vuejs so don't know very much about vuejs. But I come to know that I have to use custom vuejs directive to bind those values to model. Here are requirements of date range inputs.
One datepicker at a time.
Dynamic min max date to fullfill some validations like start<=end
Bind selected value to the modal (twoWay)
Different date format for modal and display value (if possible)
I have already spent 25 hrs on this and got too much frustrated. So If anyone knows the answer, it will be appreciated.
Here is my code.
HTML
<div id="app">
<div class="dropdown-menu">
<div class="input-group date">
<label>from:</label>
<input size="16" type="text" v-date v-model="queries.start_date" class="form_datetime" readonly="">
<span class="input-group-addon">
<span class="calendar-icon"></span>
</span>
</div>
<div class="input-group date form_datetime">
<label>to:</label>
<input size="16" type="text" v-date v-model="queries.end_date" class="form_datetime" readonly>
<span class="input-group-addon">
<span class="calendar-icon"></span>
</span>
</div>
</div>
</div>
js
Vue.directive('date', {
twoWay: true,
bind: function (el) {
$(el).datetimepicker({
format: "mm/dd/yyyy",
autoclose: true,
minView: 2,
daysShort: true
});
}
});
var vm = new Vue({
el: '#app',
data: {
queries: {
start_date: "",
end_date: "",
}
},
methods: {
testVal: function () {
console.log([this.queries.start_date, this.queries.end_date]);
}
}
});
Here is link for content.
EDIT
I have linked wrong library. I have updated library which I have used. Please check updated link.
I got solution. I have to use component. I have read somewhere that you can not get instance of custom directive in VueJS2. I don't know it is correct or not. But got my solution from this reference.
HTML
<div class="dropdown-menu" id="app">
<div class="input-group date">
<label>from:</label>
<datepicker v-model="queries.start_date" :enddate="queries.end_date"></datepicker>
<span class="input-group-addon">
<span class="calendar-icon"></span>
</span>
</div>
<div class="input-group date form_datetime">
<label>to:</label>
<datepicker v-model="queries.end_date" :startdate="queries.start_date"></datepicker>
<span class="input-group-addon">
<span class="calendar-icon"></span>
</span>
</div>
</div>
<script type="text/x-template" id="datepicker-template">
<input size="16" type="text" class="form_datetime" readonly="">
</script>
JS
Vue.component('datepicker', {
props: ['value', 'startdate', 'enddate'],
template: '#datepicker-template',
mounted: function () {
var vm = this;
$(this.$el)
.val(this.value)
.datetimepicker({
format: "mm/dd/yyyy",
autoclose: true,
minView: 2,
daysShort: true,
startDate: this.startdate,
endDate: this.enddate
})
.on('change', function () {
vm.$emit('input', this.value);
});
},
watch: {
value: function (value) {
$(this.$el).val(value);
},
startdate: function (value) {
$(this.$el).datetimepicker('setStartDate', value);
},
enddate: function (value) {
$(this.$el).datetimepicker('setEndDate', value);
}
}
});
var vm = new Vue({
el: '#app',
data: {
queries: {
start_date: "",
end_date: "",
}
},
});