I currently have a datetime field in my vue application. I want to split it up, but that would mean having to split it up in my database as well, which I am not interested in. I still want to keep the datetime column in my database table.
So my idea was to make it like 3 seperate fields: datetime, date and time. When a user has entered a date and time, these can be merged into the datetime field.
In Python you would do something like: datetime.datetime.combine( date, time )
Is there are similar method in Vue, or is there a better way of getting around this?
<b-form-row>
<b-col lg="6">
<b-form-group :label="$t('check in time')">
<time-picker v-model="guest.checkInDateTime" />
</b-form-group>
</b-col>
</b-form-row>
<b-form-row>
<b-col lg="6">
<b-form-group :label="$t('check in date')">
<date-picker v-model="guest.checkInDateTime" />
</b-form-group>
</b-col>
</b-form-row>
<b-form-row>
<b-col lg="6">
<b-form-group :label="$t('check in time and date')">
<date-time-picker v-model="guest.checkInDateTime"
:format="format"
:show-second="false"
type="datetime"/>
</b-form-group>
</b-col>
</b-form-row>
Working with dates in Javascript can be nuanced. In Javascript, assuming you have both a date value and a time value, you can easily combine these into a date string used by an html date picker like this:
const date = '2022-08-10' // string returned from date input
const time = '15:00:00' // string returned form time input
const checkInDateTime = date + 'T' + time // ISO String
If you're starting with a Date and you want to break it into a date value and a time value, it's almost easier using string manipulations than working with the Date obj, unless you're using a helper library like dayjs.
Examples:
// Assuming we have a date in ISO String format YYYY-MM-DDTHH:mm:ss
const dateFromString = dateValueFromDB.split('T')[0]
const timeFromString = dateValueFromDB.split('T')[1]
// using Date obj
const d = new Date(dateValueFromDB);
const dateFromObj = `${d.getFullYear()}-${('' + (d.getMonth() + 1)).padStart(2, '0')}-${('' + date.getDate()).padStart(2, '0')}`
const timeFromObj = `${d.getHours()}:${('' + d.getMinutes()).padStart(2,'0')}:${('' + d.getSeconds()).padStart(2,'0')}`
// using dayjs library
const dateFromDayjs = dayjs(dateValueFromDB).format('YYYY-MM-DD')
const timeFromDayjs = dayjs(dateValueFromDB).format('HH:mm:ss')
Now to answer your original question. Once we know how we're getting the correct values, in Vue.js, we can easily get and set these date values using a computed property and a custom setter.
<script>
export default {
data() {
return {
guest: {
checkInDateTime: '2022-09-10 15:00:00' // timestamp returned from database
}
}
},
computed: {
time: {
get() {
return dayjs( this.guest.checkInDateTime ).format('HH:mm:ss')
},
set(val) {
this.guest.checkInDateTime = this.date + 'T' + val
}
},
date: {
get() {
return dayjs(this.guest.checkInDateTime).format('YYYY-MM-DD')
},
set(val) {
this.guest.checkInDateTime = val + 'T' + this.time
}
}
}
}
</script>
Here's the html:
<template>
<div class="datetime-pickers">
<p>date: <input type="date" v-model="date" /></p>
<p>time: <input type="time" v-model="time" /></p>
<p>checkInDateTime: <input type="datetime-local" v-model="guest.checkInDateTime" /></p>
</div>
</template>
When any of the three values are set, the other two are updated accordingly. Here's a CodePen showing this in action:
https://codepen.io/ryanhightower/pen/ExLPobY?editors=1010
Related
I am trying something very simple in Vue and can't get it to work.. I want my API url to update with 2 new values (valueFrom and valueTwo). I am using fetch for this.
When I console log the values, they work (2021-06-17 etc). But as soon as I put them in the url, it just comes up empty.
Here are my input fields:
<label for="dateFrom" class="mr-sm-2">From</label>
<b-form-datepicker id="dateFrom" v-model="valueFrom" class="mb-2 mr-sm-4 mb-sm-0 w-25" :min="min" :max="max"></b-form-datepicker>
<label for="dateTo" class="mr-sm-2">To</label>
<b-form-datepicker id="dateTo" v-model="valueTo" class="mb-2 mr-sm-4 mb-sm-0 w-25" :min="min" :max="max"></b-form-datepicker>
Here is the button that calls the fetch:
<b-button variant="primary" #click="$fetch()">Search</b-button>
Here is my Vue code:
data() {
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const minDate = new Date(today);
const maxDate = new Date(today);
maxDate.setMonth(maxDate.getMonth() + 6);
return {
items: {},
valueFrom: '',
valueTo: '',
min: minDate,
max: maxDate
}
},
async fetch() {
const api = 'https://apiurl.com/ajax/locationSearchJSON/?location=Edinburgh&arrival=${this.valueFrom}&arrivalTime=12%3A00&departure={$this.valueTo}&departureTime=12%3A00
this.items = await fetch(api).then(res => res.json())
}
I also tried concating them in the javascript way (' + this.valueFrom + ') but that just rendered an empty string as well...
You can try using the backticks/backquotes (``) in place of the apostrophes. So your url will end up as below:
`https://apiurl.com/ajax/locationSearchJSON/?location=Edinburgh&arrival=${this.valueFrom}&arrivalTime=12%3A00&departure=${this.valueTo}&departureTime=12%3A00`
Also check the second variable this.valueTo has been added to the url correctly. In your case above this is how you've added it:
{$this.valueTo}
It should be:
${this.valueTo}
As a coding training, right now I'm making a web page where you can click a "Create" button, which triggers a popup, where you are supposed to fill in 6 data inputs, whose input style varies like text, select etc. (See the code and the attached image below)
<template>
<v-btn class="create-button" color="yellow" #click="alertDisplay">Create</v-btn>
<br/>
<p>Test result of createCustomer: {{ createdCustomer }}</p>
</div>
</template>
<script>
export default {
data() {
return {
createdCustomer: null
}
},
methods: {
alertDisplay() {
const {value: formValues} = await this.$swal.fire({
title: 'Create private customer',
html: '<input id="swal-input1" class="swal2-input" placeholder="Customer Number">' +
'<select id="swal-input2" class="swal2-input"> <option value="fi_FI">fi_FI</option> <option value="sv_SE">sv_SE</option> </select>'
+
'<input id="swal-input3" class="swal2-input" placeholder="regNo">' +
'<input id="swal-input4" class="swal2-input" placeholder="Address">' +
'<input id="swal-input5" class="swal2-input" placeholder="First Name">' +
'<input id="swal-input6" class="swal2-input" placeholder="Last Name">'
,
focusConfirm: false,
preConfirm: () => {
return [
document.getElementById('swal-input1').value,
document.getElementById('swal-input2').value,
document.getElementById('swal-input3').value,
document.getElementById('swal-input4').value,
document.getElementById('swal-input5').value,
document.getElementById('swal-input6').value
]
}
})
if (formValues) {
this.createdCustomer = this.$swal.fire(JSON.stringify(formValues));
console.log(this.createdCustomer);
}
}
}
}
</script>
Technically, it's working. The popup shows up when the "create" button is clicked, and you can fill in all the 6 blanks and click the "OK" button as well. But I want to add some functionalities that check if the user inputs are valid, I mean things like
address should be within 50 characters
firstName should be within 20 characters
customerNumber should include both alphabets and numbers
and so on.
If it were C or Java, I could probably do something like
if(length <= 50){
// proceed
} else {
// warn the user that the string is too long
}
, but when it comes to validating multiple inputs within a single popup using Vue-SweetAlert2, I'm not sure how to do it, and I haven't been able to find any page that explains detailed enough.
If it were just a single input, maybe you could use inputValidor like this
const {value: ipAddress} = await Swal.fire({
title: 'Enter an IP address',
input: 'text',
inputValue: inputValue,
showCancelButton: true,
inputValidator: (value) => {
if (!value) {
return 'You need to write something!'
}
}
})
if (ipAddress) {
Swal.fire(`Your IP address is ${ipAddress}`)
}
, but this example only involves "one input". Plus, what this checks is just "whether an IP address has been given or not" (, which means whether there is a value or not, and it doesn't really check if the length of the IP address is correct and / or whether the input string consists of numbers / alphabets).
On the other hand, what I'm trying to do is to "restrict multiple input values (such as the length of the string etc)" "within a single popup". Any idea how I am supposed to do this?
Unfortunately the HTML tags to restrict inputs (e.g. required, pattern, etc.) do not work (see this issues),
so I find two work around.
Using preConfirm as in the linked issues
You could use preConfirm and if/else statement with Regex to check your requirement, if they are not satisfied you could use Swal.showValidationMessage(error).
const{value:formValue} = await Swal.fire({
title: "Some multiple input",
html:
<input id="swal-input1" class="swal-input1" placeholder="name"> +
<input id="swal-input2" class="swal-input2" placeholder="phone">,
preConfirm: () => {
if($("#swal-input1").val() !== "Joe"){
Swal.showValidationMessage("your name must be Joe");
} else if (!('[0-9]+'.test($("#swal-input2").val())){
Swal.showValidationMessage("your phone must contain some numbers");
}
}
});
Using Bootstrap
In this way Bootstrap does the check at the validation, you need to include class="form-control" in your input class and change a little your html code.
If some conditions fails, the browser shows a validation message for each fields, in the order they are in the html code.
const {value: formValue} = await Swal.fire({
title: 'Some multiple inputs',
html:
'<form id="multiple-inputs">' +
'<div class="form-group">' +
'<input type="text" class="form-control swal-input1" id="swal-input1" min=2 max=4 required>' +
'</div>' +
'<div class="form-group">' +
'<input type="text" class="form-control swal-input2" id="swal-input2" placeholder="Name" pattern="[A-Za-z]" required>' +
'</div>' +
'</form>',
});
I have tried both the solution, actually only with Bootstrap3 but it should work also with the latest release.
I have an input where passing the value of my star-rating module. When i use my function with v-model, the value of my input change (for example '4'), but the value it's not read with my function #input.
My code in template:
<div v-if="question.element_type=='notation'">
<star-rating
inactive-color="#4c514c"
v-bind:star-size="30"
v-model="selectedNote"
#rating-selected ="next"
:rounded-corners="true"
:show-rating="false">
</star-rating>
<textarea
v-model="selectedNote"
class="myTextarea"
maxlength="1"
:name="'q'+question.id"
#input="next">
</textarea>
</div>
my two functions :
setRating: function(rating) {
this.selectedNote = rating
},
next(evt){
this.selectedNote = evt.target.value
if(evt.target.value.length)
this.affichageSuivant = true
else
this.affichageSuivant = false
},
and my state of seletedNote
selectedNote: 0,
I receive from a WebAPI a JSON object that has this property:
"BirthDate": "2018-02-14T15:24:17.8177428-03:00",
the HTML:
<input type="date" v-model="BirthDate" />
I bind that object using VueJS, but
VueJS give this message in the console:
The specified value "2018-02-14T15:24:17.8177428-03:00" does not conform to the required format, "yyyy-MM-dd".
On this case the only relevant part is 2018-02-14, I can discard the other information.
I tried to create a Two Way filter to convert that Date Time to the required format but did not have success. See VueJS two way filter
How can I convert and bind that Date/Time format to the required Date Format of the HTML date input ?
Considering myDate is your property, you can use:
<input type="date" :value="myDate && myDate.toISOString().split('T')[0]"
#input="myDate = $event.target.valueAsDate">
Since v-model is only syntactic sugar to :value and #input, you can use them instead. In this case, we used and changed them a little (to format the String that is the output of the date input to a Date object and vice-versa).
Check demo and caveats below.
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
myDate: new Date('2011-04-11T10:20:30Z')
},
methods: {
setMyDateToToday() {
this.myDate = new Date();
},
addADayToMyDate() {
if (this.myDate) // as myDate can be null
// you have to set the this.myDate again, so vue can detect it changed
// this is not a caveat of this specific solution, but of any binding of dates
this.myDate = new Date(this.myDate.setDate(this.myDate.getDate() + 1));
},
}
});
// Notes:
// We use `myDate && myDate.toISOString().split('T')[0]` instead
// of just `myDate.toISOString().split('T')[0]` because `myDate` can be null.
// the date to string conversion myDate.toISOString().split('T')[0] may
// have timezone caveats. See: https://stackoverflow.com/a/29774197/1850609
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ message }}</p>
<input type="date" :value="myDate && myDate.toISOString().split('T')[0]"
#input="myDate = $event.target.valueAsDate">
<p>
<code>
myDate: {{ myDate }}</code>
</p>
<button #click="setMyDateToToday">Set date one to today</button>
<button #click="addADayToMyDate">Add a day to my date</button>
</div>
i think this not related to vueJs , the input type="date" expected a date in YYYY-MM-DD format, or empty
see here : https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date,
it would be better if you split date object as date and time field
Correction to #acdcjunior in that this shouldn't be off by one day
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
myDate: new Date('2011-04-11T10:20:30Z')
},
methods: {
setMyDateToToday() {
this.myDate = new Date();
},
addADayToMyDate() {
if (this.myDate) // as myDate can be null
// you have to set the this.myDate again, so vue can detect it changed
// this is not a caveat of this specific solution, but of any binding of dates
this.myDate = new Date(this.myDate.setDate(this.myDate.getDate() + 1));
},
getDateClean(currDate) {
// need to convert to UTC to get working input filter
console.log(currDate);
let month = currDate.getUTCMonth() + 1;
if (month < 10) month = "0" + month;
let day = currDate.getUTCDate();
if (day < 10) day = "0" + day;
const dateStr =
currDate.getUTCFullYear() + "-" + month + "-" + day + "T00:00:00";
console.log(dateStr);
const d = new Date(dateStr);
console.log(d);
return d;
}
}
});
// Notes:
// We use `myDate && myDate.toISOString().split('T')[0]` instead
// of just `myDate.toISOString().split('T')[0]` because `myDate` can be null.
// the date to string conversion myDate.toISOString().split('T')[0] may
// have timezone caveats. See: https://stackoverflow.com/a/29774197/1850609
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ message }}</p>
<input type="date" :value="myDate && myDate.toISOString().split('T')[0]"
#input="myDate = getDateClean($event.target.valueAsDate)">
<p>
<code>
myDate: {{ myDate }}</code>
</p>
<button #click="setMyDateToToday">Set date one to today</button>
<button #click="addADayToMyDate">Add a day to my date</button>
</div>
I'm in the process of updating an old booking systems views and I am presently stuck on a solution for updating the calendar widget. As the site is responsive I have opted for the bootstrap datepicker supplied by eternicode https://github.com/eternicode/bootstrap-datepicker.
OK here the issue. I have an old Datepicker that splits the checkin & checkout dates into 3 parts and then formats the date for PHP (n = Month no leading zero)) (j = Day no leading zero) & (Y = Year 4 digit numeric).
// Initiate Params
$checkInDate = mktime(0,0,0,date("n"),date("j") + 1,date("Y"));
$checkOutDate = mktime(0,0,0,date("n"),date("j") + 3,date("Y"));
//CheckInDate
if (!isset($daysI)){
$daysI = date("j",$checkInDate);
}
if (!isset($monthsI)){
$monthsI = date("n",$checkInDate);
}
if (!isset($yearI)){
$yearI = date("Y",$checkInDate);
}
//CheckOutDate
if (!isset($daysS)){
$daysS = date("j",$checkOutDate);
}
if (!isset($monthsS)){
$monthsS = date("n",$checkOutDate);
}
if (!isset($yearS)){
$yearS = date("Y",$checkOutDate);
}
The input boxes markup is as below.
<input type='text' id='fulldate' name='fulldate'>
<label>Enter Day of Arrival (in the format DD) </label>
<input type="text" name="daysI" id="daysI" size="6" maxlength="6" />
<label>Enter Month of Arrival (in the format MM) </label>
<input type="text" name="monthsI" id="monthsI" size="6" maxlength="6" />
<label>Enter Year of Arrival (in the format YYYY) </label>
<input type="text" name="yearI" id="yearI" size="6" maxlength="6" />
Here's where I'm having the problem. The following function works with jQuery UI:
$('#fulldate').datepicker({
showAnim: 'fadeIn',
dateFormat: 'd/m/yy',
onSelect: function(dateText, inst) {
var pieces = dateText.split('/');
$('#daysI').val(pieces[0]);
$('#daysI').val(pieces[1]);
$('#daysI').val(pieces[2]);
}
});
However I cannot get a similar solution to work with the bootstrap-datepicker which I am using as a replacement for jQuery UI ie:
$('#fulldate').datepicker({
format: "d/m/yyyy",
todayBtn: "linked",
todayHighlight: true
onSelect: function(dateText, inst) {
var pieces = dateText.split('/');
$('#daysI').val(pieces[0]);
$('#monthsI').val(pieces[1]);
$('#yearI').val(pieces[2]);
}
});
Thank in advance for any solution..
The documentation gives an example of how to capture the date changed event: bootstrap-datepicker Docs - Change Date Event
Something like this should be in the right direction (untested):
$('#fulldate').datepicker()
.on('changeDate', function(ev){
var newDate = new Date(ev.date);
$('#daysI').val(newDate.getDate());
$('#monthsI').val(newDate.getMonth());
$('#yearI').val(newDate.getFullYear());
});