VueJS Date Range - vuejs2

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: "",
}
},
});

Related

Apply v-focus to the first input field on a page

I've a Vue component in which I'm trying to autofocus the first field using v-focus. But my problem is, I've dynamic components that will be included at the top of the page. So in that case how can I apply autofocus to dynamically included component?
They key is to set ref on all your inputs to the same string like this:
<input type="text" ref="myInputs"/>
Then you will have access to an array called this.$refs.myInputs inside an event handler.
So you just need to do
this.$refs.myInputs[0].focus();
new Vue({
el: "#app",
mounted() {
this.$refs.myInputs[0].focus();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div>
<div v-for="index in 3" :key="index">
<input ref="myInputs" type="text" />
</div>
</div>
</div>
It's hard to tell how you're adding the input(s) to the DOM, without any pseudo code from you, but this is one way to do it..
[CodePen mirror]
new Vue({
el: "#app",
data: {
inputs: ["firstName", "lastName"]
},
watch: {
inputs() {
this.$nextTick(() => {
this.focusFirstInput();
});
}
},
methods: {
focusFirstInput() {
let first = this.inputs[0];
let firstInput = this.$refs[first][0];
firstInput.focus();
},
handleClick() {
this.inputs.push("newInput");
}
},
mounted() {
this.focusFirstInput();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div>
<div v-for="(input, index) in inputs" :key="index">
<input :ref="input" type="text" />
</div>
<div>
<button type="button" #click="handleClick">Click to add input</button>
</div>
</div>
</div>
I found this answer on Laracast and it worked for me. All I did was insert the code below in my dynamic form field.
this.$nextTick(() => {
let index = this.items.length - 1;
let input = this.$refs.title[index];
input.focus();
});
HTML
<div id="app">
<ul v-for="item in items">
<li>
<input :ref="'title'" v-model="item.title">
</li>
</ul>
<button v-on:click="addItem">Add Item</button>
</div>
JS
let app = new Vue({
el: '#app',
data: {
items: [
{title: 'Apple'},
{title: 'Orange'},
]
},
methods: {
addItem(){
this.items.push({title: "Pineapple"});
this.$nextTick(() => {
let index = this.items.length - 1;
let input = this.$refs.title[index];
input.focus();
});
}
}
});
Note: make sure to add :ref="'title'" into your dynamic form field.
Credits to the original author of the solution.

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.

Watch date input value and use it with v-model

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

VueJS default/starting a value ina list of searched terms

Trying to make a search plugin using Vue and I'm having a problem with adding a starting/default value to the list of options. If I comment out the pair or template lines involving the start prop the rest of it works fine, but nothing renders if I leave them in.
Component Code:
Vue.component('search', {
props: {
type: String,
hidein: String,
start: {
type: Object,
default: null
}
},
//props: ['type', 'hidein', 'start'],
data: function () {
return {
search: "",
select: "",
results: [],
};
},
template: '<div #load="loaded"><input :id="hidein" type="text" v-model="search" #keyup="updateList">'+
'<input type="hidden" :name="hidein" v-model="select" class="form-control">'+
'<div v-if="start">Current: <span #click="select=start.id" :class="{\'label label-success\':(select==start.id)}>'+
'+ {{start.name}}</span></div>'+
'<div v-if="results.length">Do you mean:<ul>'+
'<li v-for="result in results" :key="result.id"><span #click="select=result.id" :class="{\'label label-success\':(select==result.id)}">'+
'+ {{result.name}}</span></li>'+
'</ul></div></div>',
methods: {
updateList: function(e) {
var response = [];
console.log("search: "+this.search);
$.ajax({
method: "GET",
url: "/api/search/"+this.type,
data: { key: this.search }
}).done(function( msg ) {
this.results = msg;
console.log(this.results);
}.bind(this));
},
loaded: function () {
this.select=!!this.start ? this.start.id : null;
}
},
});
Component Call:
<search type="ships" hidein="ship_id" ></search>
Can anyone tell me what I'm doing wrong? (Besides the hacked together template, that's hopefully a completely separate issue with the pipeline I'm having)
There is a missing " here
:class="{\'label label-success\':(select==start.id)}
But also, please, use a template literal to make your life easier.
`<div #load="loaded"><input :id="hidein" type="text" v-model="search" #keyup="updateList">
<input type="hidden" :name="hidein" v-model="select" class="form-control">
<div v-if="start">
Current:
<span #click="select=start.id" :class="{'label label-success':(select==start.id)}">
{{start.name}}
</span>
</div>
<div v-if="results.length">
Do you mean:
<ul>
<li v-for="result in results" :key="result.id">
<span #click="select=result.id" :class="{'label label-success':(select==result.id)}">
{{result.name}}
</span>
</li>
</ul>
</div>
</div>`

Extend Vue function in component to display ID

I have a Vue component and I am using internalValue to access the value attribute. How would I extend this to also get the ID?
ie internalValue = value, id
I have tried but I don't know how to add this inside the internalValue function. I've even tried to only get the ID by changing all instances of value to id but it still spits out the value.
I'd be happy to have them as one ie value, id or access them like data.value and data.id
Initialise Vue
new Vue({
el: '#topic',
data: {
selectedTopic: null
}
});
Use Component
<div class="form-group" id="topic">
<topic v-model="selectedTopic"></topic>
</div>
Register Component
Vue.component('topic', require('./components/Topicselect.vue'));
Component
<template>
<div>
<label v-for="topic in topics" class="radio-inline radio-thumbnail">
<input type="radio" v-model="internalValue" name="topics_radio" :id="topic.id" :value="topic.name">
<span class="white-color lg-text font-regular text-center text-capitalize">{{ topic.name }}</span>
</label>
<ul class="hidden">
<li>{{ internalValue }}</li>
</ul>
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
internalValue: this.value,
topics: []
}
},
mounted(){
axios.get('/vuetopics').then(response => this.topics = response.data);
},
watch: {
internalValue(v){
this.$emit('input', v);
console.log('Topicselect: the value is ' + this.internalValue);
}
}
}
</script>
Use the selected topic as your value. Basically, eliminate internalValue altogether, and just emit the topic associated with any given radio button when it's clicked. This will satisfy v-model, since it listens to input events (unless you customize it).
export default {
props: ['value'],
data () {
return {
topics: []
}
},
methods:{
selectValue(topic){
this.$emit('input', topic)
}
},
mounted(){
axios.get('/vuetopics').then(response => this.topics = response.data);
}
})
And your template
<template>
<div>
<label v-for="topic in topics" class="radio-inline radio-thumbnail">
<input type="radio" #click="selectValue(topic)" name="topics_radio" :id="topic.id" :value="topic.name" :checked="value && topic.id == value.id">
<span class="white-color lg-text font-regular text-center text-capitalize">{{ topic.name }}</span>
</label>
<ul class="hidden">
<li>{{ value }}</li>
</ul>
</div>
</template>
This will set selectedTopic in your Vue to a topic, which is something like
{
id: 2,
name: "some topic"
}
based on how you use it in your template.
Working example.