How to catch events is vue.js v-calendar - javascript

I am using the vuejs v-calender plugin so I have a daterange picker. It all renders fine and I can select dates but that is is it.
What I want to do is just log out the selected dates so later I can store them in database, update form etc but I don't know how to achieve this. I can't find any examples in the documentation of how to do this.
Does anyone know how to get the start and end dates of a selected date range?
Here is what I have so far...
<template>
<v-date-picker mode='range' v-model='range' is-inline :columns="$screens({ default: 1, lg: 2 })" />
</template>
<script>
import { DatePicker } from 'v-calendar'
export default {
name: 'Booking',
components: {
DatePicker
},
data() {
return {
range: {
start: new Date(),
end: null
}
}
},
mounted() {
this.$root.$on('input', (value) => {
console.log('dxggdfg');
});
}
}
</script>

Add input event
<v-date-picker mode='range' v-model='range' #input="onDateRangeChange" is-inline :columns="$screens({ default: 1, lg: 2 })" />
{
...
methods: {
onDateRangeChange() {
console.log(this.range)
}
},
mounted() {
this.$root.$on('input', (value) => {
console.log('dxggdfg');
});
}
}
Alternatively you can use watch, which works well if you also update your v-model externally:
{
...
watch: {
range: {
handler: function () {
console.log(this.range)
},
deep: true
}
},
mounted() {
this.$root.$on('input', (value) => {
console.log('dxggdfg');
});
}
}

For me, it worked with #input
For example:
<date-picker mode='range' v-model='range' is-range class="m-auto" #input="changeDate"/>
Then, in the methods, I just have a method called "changeDate". This will be called once the second date has been selected in the date range.

Stumbled across this - hopefully will help someone else.
When using just single dates, you can set things up like this.
<v-date-picker
is-expanded
:columns="$screens({ default: 1, lg: 2 })"
:attributes="calendar.attributes"
v-model="calendar.today"
#dayclick="changeDate"
/>
...
methods: {
changeDate() {
console.log('changeDate called');
// emit your own event or ??
},
}
...

computed: {
dateRange: {
set (val) {
// action after date change here
},
get () {
let range = {
start: new Date(moment().subtract(7, 'day').format('YYYY-MM-DD')),
end: new Date(moment().format('YYYY-MM-DD'))
}
// action after receives date from props
return range
}
}
}
<v-date-picker
:columns="$screens({ default: 1, lg: 2 })"
is-range
:value="dateRange"
:attributes="attrs"
:max-date='new Date()'
v-model="dateRange"
/>
You can achieve the result with computed, I waste a lot of time with data() but it didn't work for me

Related

VueJS Duplicate components all updating at the same time

Might be a simple solution, but I'm currently not seeing it. I have an object that describes several configurations.
Object looks like this:
export const fieldSelectionDefault = {
cohort: {
currency_key: null,
salary_key: null,
timeframe_key: null
},
school: {
currency_key: null,
salary_key: null,
timeframe_key: null,
response_count_key: null,
},
}
export const cohortListFieldDefault = {
field_student: { ...fieldSelectionDefault },
field_alum_1: { ...fieldSelectionDefault },
field_alum_2: { ...fieldSelectionDefault },
field_alum_3: { ...fieldSelectionDefault },
}
Now, I have a parent component where I have a form. This form will list each field_* to have a <CohortFieldConfig /> component where we can input the values of the fieldSelectionDefault.
In the parent form, I add them like this:
<h5>Student</h5>
<CohortFieldConfig
:key="'settings.field_student'"
:disabled="settings.active_entities.student"
:selection-fields="settings.field_student"
#update-fields="(val) => test(val, 'stu')"
/>
<h5>Alumnus 1</h5>
<CohortFieldConfig
:key="'settings.field_alum_1'"
:disabled="settings.active_entities.alum_1"
:selection-fields="settings.field_alum_1"
#update-fields="(val) => test(val, 'alum')"
/>
CohortFieldConfig looks like this (example of one inputs, removed js imports):
<template>
<div>
<a-form-item label="Currency input">
<a-input
:disabled="!disabled"
placeholder="Select a currency form key"
v-model="objSelectionFields.cohort.currency_key"
/>
</a-form-item>
<FieldSelector
#select="val => (objSelectionFields.cohort.currency_key = val)"
:user="user"
:disabled="!disabled"
/>
</div>
</template>
<script>
export default {
name: 'CohortFieldConfig',
components: { FieldSelector },
props: {
selectionFields: {
type: [Object, null],
default: () => {
return { ...fieldSelectionDefault }
},
},
disabled: {
type: Boolean,
default: () => false,
},
},
data: function() {
return {
fieldSelectionDefault,
objSelectionFields: { ...this.selectionFields },
}
},
watch: {
objSelectionFields: {
handler(){
this.$emit('update-fields', this.objSelectionFields)
},
deep: true
}
},
methods: {
update() {
// not really used atm
this.$emit('update-fields', this.objSelectionFields)
},
},
}
</script>
When you type in the input, BOTH are updated at the same time. For student & alum_1.
The update-fields event is fired for both (same) components
Whats the reason? I've tried setting different key, doesn't work.
UPDATE
As pointed out in the comments, the issue was I was giving the same object. To correct this, I make a (deep) copy of the object as so:
export const cohortListFieldDefault = {
field_student: JSON.parse(JSON.stringify(fieldSelectionDefault)),
field_alum_1: JSON.parse(JSON.stringify(fieldSelectionDefault)),
field_alum_2: JSON.parse(JSON.stringify(fieldSelectionDefault)),
field_alum_3: JSON.parse(JSON.stringify(fieldSelectionDefault)),
}

end date must be less than start date, Vuelidate and VueJs

I'm having problems doing a form validation. I have a start date and an end date. I need to confirm that the end date is greater than the start date.
I'm using vuejs and vuelidate.
I'm working with an Input.vue component, which has a standard input being reused in other forms. I also have a Form.vue component that has Input.vue inside. And this Form.vue component is called both in the modal of creating a new event and in the editing mode.
My problem is in the validation of when the event is edited. If you select an end date less than the start date, it doesn't allow saving the information, that's ok. However, when trying to correct it by selecting a date greater than the start date, and clicking on save again, it seems that it is no longer calling validation. With this, the error that the end date must be greater than the initial one does not disappear. I did several tests and noticed that validation is not called after the error occurs when editing. I do not know why.
component Input.vue:
<template>
<b-form-group :label="label" :label-for="labelFor">
<b-form-input
:id="labelFor"
v-model="value"
:type="type"
:placeholder="placeholder"
:class="{ 'is-invalid': this.isInvalid }"
required>
</b-form-input>
<slot></slot>
</b-form-group>
</template>
<script>
export default {
name: 'InputForm',
props: [ 'label', 'labelFor', 'vModel', 'type', 'placeholder', 'isInvalid' ],
computed: {
value: {
get() {
return this.vModel
},
set(value) {
this.newValue = value
return this.$emit('emitValue', this.newValue)
}
}
},
}
</script>
component Form.vue:
<template>
<b-form>
....
<InputForm
class="col-8 pl-0"
:label="'*Início'"
:labelFor="'event_start_at_date'"
:vModel="event.start_at_date"
:type="'date'"
#emitValue="($event) => event.start_at_date = $event"
/>
<InputForm
class="col-4 pr-0"
v-if="!event.all_day"
:labelFor="'event_start_at_time'"
:vModel="event.start_at_time"
:type="'text'"
#emitValue="($event) => event.start_at_time = $event"
/>
</b-col>
<b-col cols="12" lg="6" class="d-flex align-items-end">
<InputForm
class="col-8 pl-0"
:label="'*Fim'"
:labelFor="'event_end_at_date'"
:type="'date'"
:vModel="event.end_at_date"
#emitValue="($event) => event.end_at_date = $event"
:isInvalid="invalid.end_at_date.$error"
>
<b-form-invalid-feedback v-if="this.submitted" :state="invalid.end_at_date.$error == 0">Fim deve ser maior que início.</b-form-invalid-feedback>
</InputForm>
<InputForm
class="col-4 pr-0"
v-if="!event.all_day"
:labelFor="'event_end_at_time'"
:vModel="event.end_at_time"
:type="'text'"
:isInvalid="invalid.end_at_date.$error"
:class="{ 'error' : invalid.end_at_date.$error }"
#emitValue="($event) => event.end_at_time = $event"
/>
...
</b-form>
</template>
export default {
name: 'Form',
props: [ 'event', 'event_shared', 'error', 'submitted' ],
components: { Input },
computed: {
invalid() {
if(this.error.edit_event != undefined) {
return this.error.edit_event
} else if(this.error.event != undefined) {
return this.error.event
} else {
return false;
}
},
stateInvalid() {
if(this.error.edit_event != undefined) {
return this.error.edit_event
} else if(this.error.event != undefined) {
return this.error.event
} else {
return 0;
}
},
},
watch: {
event() {
let newEvent = this.event
this.$emit('emitObj', newEvent)
},
},
}
</script>
component ModalEditEvent.vue:
...
<VWarningErrorForm v-if="$v.$error" />
<FormEvent
:event="edit_event"
:event_shared="get_shared_users_id"
:error="this.$v"
:submitted="this.submitted"
#emitObj="($event) => edit_event = $event"
/>
...
<b-button
v-if="editEvent && !view_popover_save"
variant="primary" type="submit" #click="save()">Gravar</b-button>
...
<script>
import moment from 'moment';
import { required } from "vuelidate/lib/validators";
import isAfterDate from '#/functions/isAfterDate'
...
validations: {
edit_event: {
name: {
required
},
end_at_date: {
required: function(value) {
return isAfterDate(value, this.edit_event);
}
},
},
},
...
save() {
this.submitted = true;
this.$v.$touch();
if(!this.$v.$error) {
....
}
},
validation
import moment from 'moment';
function isAfterDate(value, vm) {
let hour = function(att) {
return `${att[0]}${att[1]}`;
}
let minute = function(att) {
return `${att[3]}${att[4]}`;
}
let start = moment(vm.start_at_date).set({hour: hour(vm.start_at_time), minute: minute(vm.start_at_time) })._d
let end = moment(vm.end_at_date).set({hour: hour(vm.end_at_time), minute: minute(vm.end_at_time) })._d
return end >= start;
}
export default isAfterDate;
Solved my issue with Vue.set. I realized that I was trying to detect the change of a property that I added later myself. So I had to use Vue.set when adding the property, so I could detect the change later, and then correctly validate the form. Thanks to whoever tried to help me!

vuejs: vue-select component not updating values

I am trying to use the vue-select component for a dropdown list. So far I have written.
<template>
<div>
<v-select label="name" key="id" :v-model="selected" :reduce="data => data.id" :options="items" #input="update()" />
</div>
</template>
<script>
export default {
props: {
initial: {
type: [String, Number],
default: 0,
},
api_call: {
type: String,
required: true,
},
},
data(){
return {
value: this.initial,
items: [],
}
},
computed: {
selected: {
get() {
return this.value;
},
set(val) {
return this.value = val;
}
},
},
methods:{
update() {
console.log('selected', this.selected, this.value);
this.$emit('input', this.selected);
},
getData: function(){
axios.get('/api/' + this.api_call)
.then(function (response) {
this.items = response.data;
}.bind(this));
},
},
created(){
this.getData();
}
}
The dropdown list populates as intended and selecting an Item inserts it in the input filed. The two problems I have are
Neither the value nor the selected variables change when something is selected.
I am also passing in an initial value which I would like to be selected as the default in the list.
Remove the binding sign : from v-model directive
<v-select label="name" key="id" v-model="selected" :reduce="data => data.id" :options="items" #input="update()" />
and init your value like :
data(vm){//vm refers to this
return {
value: vm.initial,
items: [],
}
},
or :
data(){
return {
value: null,
items: [],
}
},
mounted(){
this.value=this.initial
}

Vue.js2 - Object.assign({}, this.var) preventing watch method

returning this.user (a global computed property) works as expected. Of course, I'm making a copy because I do not want to overwrite the actual user data. So, I'm using Object.assign. However, once I include return Object.assign({}, this.user) (opposed to this.user), the watch method no longer functions.
Here is my template (I am using bootstrap-vue):
<template>
<form role="form">
<b-form-group
label="First Name"
label-for="basicName"
:label-cols="3"
:horizontal="true">
<b-form-input id="user-name-first" type="text" v-model="userFormData.fname"></b-form-input>
</b-form-group>
<b-form-group
label="Last Name"
label-for="basicName"
:label-cols="3"
:horizontal="true">
<b-form-input id="user-name-lirst" type="text" v-model="userFormData.lname"></b-form-input>
</b-form-group>
<b-form-group
label="Email"
label-for="user-email"
:label-cols="3"
:horizontal="true">
<b-form-input id="user-email" type="text" v-model="userFormData.email"></b-form-input>
</b-form-group>
<b-form-group
:label-cols="3"
:horizontal="true">
<b-button type="submit" variant="primary">Save changes</b-button>
<b-button type="button" variant="secondary" #click="userFormCancel">Cancel</b-button>
</b-form-group>
</form>
</template>
So, this works and sets editsPending to true whenever changes are applied to userProfile (via v-model on an input)
<script>
export default {
name: 'userProfile',
data () {
return {
editsPending: false
}
},
computed: {
userFormData: function () {
return this.user
}
},
watch: {
userFormData: {
deep: true,
handler (val) {
this.editsPending = true
}
}
},
methods: {
userFormCancel () {
this.editsPending = false
}
}
}
</script>
...but this does not; userFormData becomes a clone of user but editsPending is not affected by updates to userFormData.
<script>
export default {
name: 'userProfile',
data () {
return {
editsPending: false
}
},
computed: {
userFormData: function () {
return Object.assign({}, this.user)
}
},
watch: {
userFormData: {
deep: true,
handler (val) {
this.editsPending = true
}
}
},
methods: {
userFormCancel () {
this.editsPending = false
}
}
}
</script>
Can anyone explain why this may be happening and suggest a viable solution?
A computed property will only re-evaluate when some of its
dependencies have changed. (source)
That's why it works with return this.user and not with Object.assign because it's not a reactive dependency.
If you want reactive data you should initialize userFormData as an empty object data and assign your user when your Vue instance is created:
data () {
return {
editsPending: false,
userFormData: {}
}
},
created() {
this.userFormData = Object.assign({}, this.user)
},
Tested different things to reproduce the behaviour you see.
I suspect that in your template your are binding your inputs to UserFormdata (incorrect)
<input v-model="userFormData.name">
Instead of (correct)
<input v-model="user.name">
If you could share your template that would help ;)
Edit: After template addition.
new Vue({
el: '#app',
data () {
return {
editsPending: false,
user: { name: 'John Doe' },
userCachedData: {},
}
},
created() {
this.userCachedData = Object.assign({}, this.user);
},
watch: {
user: {
deep: true,
handler (val) {
this.editsPending = true
}
}
},
methods: {
userFormCancel () {
this.editsPending = false
}
}
})
<div id="app">
{{ user }}
{{ userCachedData }}
<br>
<input v-model="user.name" />
{{ this.editsPending }}
</div>
Codepen: https://codepen.io/aurelien-bottazini/pen/BVNJaG?editors=1010
You can use the $emit to assign value to object:
mounted() {
this.$emit("userFormData", this.user);
}

How to pass property value between to instances of a component

I have to datepicker components, from date and to date.
i want to update the to date with the from date if the to date is empty.
I've looked at doing emit on the value but feel this is not right and i'm not sure how to do it
<datepicker
input-label="From"
input-id="start-date"
input-name="start_date"
input-value="<%= group_discount.start_date %>"
#change-date="changeDate"
>
</datepicker>
<datepicker
input-label="To"
input-id="end-date"
input-name="end_date"
input-value="<%= group_discount.end_date %>">
</datepicker>
import Vue from "vue"
import Datepicker from "../components/DatePicker"
Vue.use(Datepicker)
const initGroupDiscount = () => {
new Vue({
el: "#js-group-discounts",
components: {
Datepicker,
},
methods: {
changeDate(value) {
console.log("value")
console.log(value)
},
},
})
}
document.addEventListener("DOMContentLoaded", () => {
initGroupDiscount()
})
<template>
<div >
<label :for="this.inputId">{{ this.inputLabel }}</label>
<input type="text"
class="form-control form-control-info"
placeholder="dd/mm/yyyy"
:name="this.inputName"
:id="this.inputId"
pattern="\d{1,2}/\d{1,2}/\d{4}"
required
v-model="isInput"
v-on:keyup="updateCalendar($event)"
ref="dateinput"
#blur="blur"
#focus="focus">
<datepicker format="dd/MM/yyyy"
input-class="form-control"
placeholder="dd/mm/yyyy"
v-model="isPicker"
:inline="true"
v-show="isOpen"
#mouseover.native="mouseOver"
#mouseleave.native="mouseLeave"
#selected="updateInput"></datepicker>
</div>
</template>
<script>
import Vue from "vue"
import Datepicker from "vuejs-datepicker"
Vue.use(Datepicker)
export default {
name: "neptune-datepicker",
props: {
inputLabel: {
type: String,
},
inputId: {
type: String,
},
inputValue: {
type: String,
},
inputName: {
type: String,
},
},
data(){
let value = ""
if (this.inputValue) {
const dateParts = this.inputValue.split("-")
value =`${dateParts[2]}/${dateParts[1]}/${dateParts[0]}`
}
return {
isInput: value,
isPicker: this.inputValue,
isOpen: false,
}
},
components: {
Datepicker
},
methods: {
updateInput(date) {
this.isInput = date.toLocaleDateString("en-GB")
this.$emit('changeDate', this.isInput);
},
updateCalendar(event) {
const dateString = event.srcElement.value
if (dateString.length === 10) {
const dateParts = dateString.split("/")
const dateObject = new Date(
dateParts[2],
dateParts[1],
dateParts[0],
)
if ((dateObject !== "Invalid Date") && !Number.isNaN(dateObject)) {
this.isPicker = dateObject
}
}
},
blur() {
this.isOpen = false
},
focus() {
this.$refs.dateinput.focus()
this.isOpen = true
},
mouseOver() {
this.$refs.dateinput.focus()
this.isOpen = true
},
mouseLeave() {
this.$refs.dateinput.focus()
this.isOpen = true
},
},
}
</script>
it emits the correct value to the console but i don't know how i would pass this to only that particular instance of the component, the "To" date
The parent component should pass a prop to both data sub-components:
<datepicker
:myValue="myValue"
input-label="From"
input-id="start-date"
input-name="start_date"
input-value="<%= group_discount.start_date %>"
#change-date="changeDate"
>
</datepicker>
<datepicker
:myValue="myValue"
input-label="To"
input-id="end-date"
input-name="end_date"
input-value="<%= group_discount.end_date %>">
</datepicker>
Then you can use that prop in a computed or method to validate if it contains a value or not and override the to or from value.
Edit: You can also use your emit strategy but you still need to pass a prop to one of the components so it can read the data. If you've never used props before read about them here: https://v2.vuejs.org/v2/guide/components-props.html

Categories