Vue JS date of birth picker - vue.js

I'm setting up a date of birth field.
How can I set the "Year" as my first popup window? instead of calendar view popup.
The view package I'm using is "vue2-datepicker"
I would like to choose the "YEAR" first and then "Month" and then "Date"
This is what I want to show up first when I click the calendar icon.
At the moment, when I click the calendar icon it show the default calendar.
Here's the code I have at the moment. It's working fine with the default calendar but I just want to default to the year selection first and then Month and then date.
<template>
<div class="styled-form__field">
<label class="styled-form__label">{{ label }}</label>
<span
:class="['styled-form__error', {'is-visible': error}]"
>{{ error }}</span>
<date-picker
:value="value"
lang="en"
:format="'DD MMMM YYYY'"
:placeholder="' '"
:width="'auto'"
:input-class="datePickerClass"
:not-before="datePicker.start"
:not-after="datePicker.finish"
#input="changeHandler"
></date-picker>
</div>
</template>
<script>
import moment from 'moment';
import DatePicker from '../common/DatePicker.vue';
export default {
components: {
DatePicker
},
props: {
value: {
required: true
},
label: String,
error: String
},
data() {
return {
datePicker: {
start: moment().subtract(115, 'years').toDate(),
finish: moment().subtract(1, 'days').toDate()
}
};
},
computed: {
datePickerClass() {
return this.error ? 'styled-form__date-picker is-invalid' : 'styled-form__date-picker';
}
},
methods: {
changeHandler(newValue) {
let parsedValue = '';
if (newValue !== '') {
parsedValue = moment(newValue).format('YYYY-MM-DD');
}
this.$emit('input', parsedValue);
}
}
};
</script>

The standard browser datepicker doesn't let you alter its behaviour like that. You might find another datepicker component that suits you better, but I suspect you will have to write your own, combining separate inputs for year, month and date. The effort will only be worth it if you're super-picky about the result. For most situations, you will be better off with a ready-made component.

Related

How to use :value and v-model together

I need to use :value and v-model together. I've got a form and on the following component I'm receiving an 'age' as a query parameter because I'm filling a previous input somewhere else. I want that age to auto-populate the input on this component. But what if the user changes that value? I'd like to re-store that new age and emit it to the parent .Vue file. How can I do this?
In other words: what if I'm getting a '3' as the age, the auto-populated input shows a '3' but then the user notices they wanted to write '30' instead of '3'? How can I save 30 instead of '3'?
Thanks in advance
<template>
<div>
<p>age</p>
<input type="text" :value="age" v-model="age_keyup" #keyup="send()">
</div>
</template>
<script>
export default {
data: function () {
return {
age_keyup: null,
}
},
methods: {
send(){
this.$emit("age_keyup", this.age);
}
},
props: ['age']
}
</script>
No need to bind the value to that prop just init age_keyup based on age :
<template>
<div>
<p>age</p>
<input type="text" v-model="age_keyup" #keyup="send()">
</div>
</template>
<script>
export default {
data: function () {
return {
age_keyup:null,
}
},
methods: {
send(){
this.$emit("age_keyup", this.age_keyup);
}
},
props: ['age'],
mounted(){
this.age_keyup=this.age
}
}
</script>

How do I set a default value for v-model using computed property

I have two input fields for dates: to and from.
I want the user to be able to select dates that I will send a request to the backend. That is why I am using a computer property - in the setter I will validate the dates to make sure from is less than to and to is not less than from.
How do I set default values for those inputs?
I am using computed properties so I can set default values for both inputs. One value is set to now, the other is 1 week ago.
<template>
<div class="logs-listing-table">
<div class="mb-1 filter-actions-wrapper">
<input type="date" v-model="this.fromDate">
<input type="date" placeholder="this.toDate">
</div>
</div>
</template>
<script>
export default {
name: 'BnbReport',
data () {
return {
fromDate: this.fromDateDefault(),
toDate: this.toDateDefault()
}
},
computed: {
fromDateDefault () {
var previousPeriod = new Date()
previousPeriod.SetDate(previousPeriod.getDate() - 7)
previousPeriod.toISOString().substr(0, 10)
this.fromDate = previousPeriod
return this.fromDate
},
toDateDefault () {
var today = new Date()
today.toISOString().substr(0, 10)
this.toDate = today
return this.toDate
}
}
}
</script>
However, eslint throws me an error:
Unexpected side effect in "fromDateDefault" computed property
Unexpected side effect in "toDateDefault" computed property
You can add get and set functions into your computed property.
HTML:
<input type="date" v-model="fromDateDefault">
JS:
computed: {
fromDateDefault: {
get() {
//your validation
return this.fromDate;
},
set(val) {
this.fromDate = val;
},
},
}
You can read more about the setters and getters in the official doc:
https://v3.vuejs.org/guide/computed.html#computed-properties

VueJS: how to trigger 'change' on <input> changed programmatically

I'm going to build a customized virtual keyboard, so that's the first problem I've encountered.
I have an input element, whose value is changed from outside, in my case by pressing a button. The problem is that there seems to be no way to trigger the normal 'change' event.
Neither clicking outside the input, nor pressing Enter gives any result. What might be the correct way of solving this problem?
<template>
<div class="app-input">
<input #change="onChange" type="text" v-model="value" ref="input">
<button #click="onClick">A</button>
</div>
</template>
<script>
export default {
name: "AppInput",
data() {
return {
inputDiv: null,
value: ""
};
},
props: {
msg: String
},
methods: {
onClick() {
this.value = this.value + "A";
this.inputDiv.focus();
},
onChange() {
alert("changed");
}
},
mounted() {
this.$nextTick(() => {
this.inputDiv = this.$refs.input;
});
}
};
</script>
The whole pen can be found here.
v-on:change would only trigger on a direct change on the input element from a user action.
What you are looking for is a wathcer for your data property, whenever your value changes, watcher will execute your desired function or task.
watch: {
value: function() {
this.onChange();
}
}
The watch syntax is elaborated on the provided official vuejs docs link. use your data property as the key and provide a function as a value.
Check the snippet.
export default {
name: "AppInput",
data() {
return {
inputDiv: null,
value: ""
};
},
props: {
msg: String
},
methods: {
onClick() {
this.value = this.value + "A";
this.inputDiv.focus();
},
onChange() {
alert("changed");
}
},
// this one:
watch: {
value: function() {
this.onChange();
}
},
// --- rest of your code;
mounted() {
this.$nextTick(() => {
this.inputDiv = this.$refs.input;
});
}
};
When I build any new vue application, I like to use these events for a search input or for other inputs where I don't want to fire any functions on #change
<div class="class">
<input v-model="searchText" #keyup.esc="clearAll()" #keyup.enter="getData()" autofocus type="text" placeholder="Start Typing ..."/>
<button #click="getData()"><i class="fas fa-search fa-lg"></i></button>
</div>
These will provide a better user experience in my opinion.

Is it possible to detect if a change event was triggered by a click on a Vue select element?

I have a <select>-element that has a data property bound to it using v-model in Vue.
Sometimes I want to change that value dynamically. I also have an event-listener attached to this element which is triggered on the change-event. See code example:
<template>
<div class="mySelector">
<select id="testSelect" v-model="mySelectModel"
#change="onChange($event)">
<template v-for="(item, index) in someList">
<option :class="['btn', 'btn-default', 'removing-button']" :value="index">{{item.name}}</option>
</template>
</select>
</div>
</template>
<script>
export default {
data() {
return {
mySelectModel: null
}
},
props: {
},
methods: {
customChange: function() {
this.mySelectModel = ... // some value we from somewhere else that is set dynamically on some condiftion
},
onChange: function (event) {
if (!event) return;
// DO SOMETHING THAT WE ONLY WANT TO DO ON A REAL CLICK
}
},
}
</script>
The problem I have is that when I change the data value mySelectModel dynamically, like in the customChange-method, the change event is also called, triggering the method onChange. I only want to do stuff in that method if it was really triggered by a real click, not when it was changed dynamically.
I can not find a way to distinguish between those cases when the change-event is triggered by a click or when it is just changed for some other reason. Any suggestions?
See vue-js-selected-doesnt-triggering-change-event-select-option, it appears that select does not trigger #change when v-model is updated by JS (only when the selected value is changed by user).
A directive can add the functionality
Vue.directive('binding-change', {
update: function (el, binding, vnode) {
const model = vnode.data.directives.find(d => d.name === 'model')
if (model) {
binding.value(model.value)
}
}
})
use like
<select id="testSelect"
v-binding-change="onChange"
v-model="mySelectModel"
#change="onChange($event)">
Not sure about the parameter to onChange - I'll give it a test.
Similar to this suggested solution, you can make a settable computed that you v-model in your widget:
The get function simply returns the data item
The set function does whatever you want a change in the widget to do, in addition to setting the data item
Other code can change the data item directly and will not execute the set code of the computed.
new Vue({
el: '#app',
data: {
values: ['one','two','three'],
selectedItem: 'two'
},
computed: {
wrappedSelectedItem: {
get() { return this.selectedItem; },
set(value) {
console.log("Changed in widget");
this.selectedItem = value;
}
}
},
methods: {
changeToThree() {
console.log("Stealth change!");
this.selectedItem = 'three';
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<select v-model="wrappedSelectedItem">
<option v-for="value in values" :value="value">{{value}}</option>
</select>
<button #click="changeToThree">Set to three</button>
</div>

VueJs: Textarea input binding

I'm trying to figure out how to detect change of the value on the textarea from within the component.
For input we can simply use
<input
:value="value"
#input="update($event.target.value)"
>
However on textarea this won't work.
What I'm working with is the CKEditor component, which should update wysiwyg's content when model value of the parent component (attached to this child component) is updated.
My Editor component currently looks like this:
<template>
<div class="editor" :class="groupCss">
<textarea :id="id" v-model="input"></textarea>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
},
id: {
type: String,
required: false,
default: 'editor'
}
},
data() {
return {
input: this.$slots.default ? this.$slots.default[0].text : '',
config: {
...
}
}
},
watch: {
input(value) {
this.update(value);
}
},
methods: {
update(value) {
CKEDITOR.instances[this.id].setData(value);
},
fire(value) {
this.$emit('input', value);
}
},
mounted () {
CKEDITOR.replace(this.id, this.config);
CKEDITOR.instances[this.id].setData(this.input);
this.fire(this.input);
CKEDITOR.instances[this.id].on('change', () => {
this.fire(CKEDITOR.instances[this.id].getData());
});
},
destroyed () {
if (CKEDITOR.instances[this.id]) {
CKEDITOR.instances[this.id].destroy()
}
}
}
</script>
and I include it within the parent component
<html-editor
v-model="fields.body"
id="body"
></html-editor>
however, whenever parent component's model value changes - it does not trigger the watcher - effectively not updating the editor's window.
I only need update() method to be called when parent component's model fields.body is updated.
Any pointer as to how could I approach it?
That's a fair bit of code to decipher, but what I would do is break down the text area and the WYSIWYG HTML window into two distinct components and then have the parent sync the values, so:
TextArea Component:
<template id="editor">
<textarea :value="value" #input="$emit('input', $event.target.value)" rows="10" cols="50"></textarea>
</template>
/**
* Editor TextArea
*/
Vue.component('editor', {
template: '#editor',
props: {
value: {
default: '',
type: String
}
}
});
All I'm doing here is emitting the input back to the parent when it changes, I'm using input as the event name and value as the prop so I can use v-model on the editor. Now I just need a wysiwyg window to show the code:
WYSIWYG Window:
/**
* WYSIWYG window
*/
Vue.component('wysiwyg', {
template: `<div v-html="html"></div>`,
props: {
html: {
default: '',
type: String
}
}
});
Nothing much going on there, it simply renders the HTML that is passed as a prop.
Finally I just need to sync the values between the components:
<div id="app">
<wysiwyg :html="value"></wysiwyg>
<editor v-model="value"></editor>
</div>
new Vue({
el: '#app',
data: {
value: '<b>Hello World</b>'
}
})
Now, when the editor changes it emits the event back to the parent, which updates value and in turn fires that change in the wysiwyg window. Here's the entire thing in action: https://jsfiddle.net/Lnpmbpcr/