Can't return empty value to input box on vue - vue.js

I want made a validation for input to be number only, whenever someone input a string, the input box will be cleared.
First, I made a method function on $event like this (ps. I use props)
<BaseInput
:value="nama"
#update="nama = ruled($event)"
label="Nama"
type="type"
/>
and this is the method, I use RegExp to check if the $event value is number. When it's false then I return $event value to empty string.
ruled(event) {
console.log(event)
var intRegex = new RegExp(/[0-9]/);
var data = intRegex.test(event)
if(!data) {
alert("Value Must Number")
event = ""
console.log('masuk if' + data)
}
return event
}
but it didn't clear the input box, anyone know why it happened ?

As Creative Learner said, I must clearing input box in child component only, so I did this on my component child
<template>
<input
:value="value"
:placeholder="label"
#input="$emit('update', ruled($event))"
/>
</template>
And this is the methods:
methods: {
ruled(event) {
//console.log(event)
var val = event.target.value
if(event.target.type == "number"){
var intRegex = new RegExp(/[0-9]/);
var intdata = intRegex.test(val)
if(intdata == false) {
error = "Value must contain number"
//alert("Value must contain number")
return event.target.value = ""
}
}
return val
}
}
great thanks to Creative Learner who made me understand

Suggestions :
Instead of #update you have to use #keypress or #change.
You can use v-model for two-way data binding.
Working Demo :
new Vue({
el: '#app',
data: {
nama: ''
},
methods: {
ruled(event) {
var intRegex = new RegExp(/^\d+$/);
var data = intRegex.test(this.nama);
if (!data) {
alert("Value must contain number");
this.nama = "";
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input
v-model="nama"
#change="ruled($event)"
label="Nama"
type="text"
/>
</div>

Related

Vue2 Element-UI Datepicker force refresh for dynamic disabledDate

In the codepen below I have a Element-UI datepicker set up to show a dynamic disabled dates based on a random number.
The number of disabled dates change every time the datepicker input comes into focus.
My issue is the datepicker doesn't refresh the disabled dates until you click on a different month. The datepicker also shows the last month you were previously on when when you click off and back in.
Is there a way to force Element-UI Datepicker to refresh? I would like to make the datepicker refresh in the on focus event after the new disabled value is set.
https://codepen.io/anon/pen/rbXjLr
Element-UI Datepicker Documentation
<div id="app">
<template>
<div class="block">
<span class="demonstration">Picker with quick options</span>
<el-date-picker
v-model="value2"
type="date"
placeholder="Enter Date"
#focus="focus()"
:default-value="defaultValue"
:picker-options="pickerOptions">
</el-date-picker>
</div>
</template>
</div>
var is10Days = "";
var Randomizer = (function(){
var is10DaysSetter = function(){
if(is10Days === "") {
is10Days = Math.round(Math.random()) === 1;
}
//console.log("is10Days: " + is10Days);
return is10Days;
}
return {
Is10DaysSetter: is10DaysSetter
}
})()
var Main = {
data() {
return {
defaultValue: "",
pickerOptions: {
disabledDate(time) {
var self = this;
var date = moment()._d;
var mindate = moment().subtract(5,'d')._d;
var maxDate = moment()._d;
var isBeforeMinDate = time.getTime() < mindate;
var isAfterMaxDate = time.getTime() > maxDate;
if(is10Days !== "" && is10Days){
var alternateMinDate = date.setDate(date.getDate() - 10);
isBeforeMinDate = time.getTime() < alternateMinDate;
}
//console.log("disabledDate");
return isBeforeMinDate || isAfterMaxDate;
}
},
value2: '',
};
},
methods:{
focus: function() {
var self = this;
is10Days = "";
self.defaultValue = moment().format("YYYY-MM-DD");
Randomizer.Is10DaysSetter();
console.log("reset is10Days: " + (is10Days ? "10 days" : "5 days"));
}
}
};
var Ctor = Vue.extend(Main)
ELEMENT.locale(ELEMENT.lang.en)
new Ctor().$mount('#app')
I posted this as a feature request on Element UI's git hub and received a response:
https://github.com/ElemeFE/element/issues/15380
<el-date-picker
ref="picker" //Added this
v-model="value2"
type="date"
placeholder="Enter Date"
#focus="focus()"
:picker-options="pickerOptions">
</el-date-picker>
methods:{
focus: function() {
var self = this;
is10Days = "";
Randomizer.Is10DaysSetter();
//Added this
this.$nextTick(_ => {
this.$refs.picker.picker.date = new Date()
})
console.log("reset is10Days: " + (is10Days ? "10 days" : "5 days"));
}
}
Adding a reference to picker allowed me to override the unwanted feature of going back to the previously viewed month and solved my issue. This came with a warning that since this is not part of the public API, it could change in a future version.
Here is a link to a working code pen:
https://codepen.io/steveshore/pen/rbXjLr
I think it is about compomemt render. when your main vue app initialize, <el-date-picker>
rendered completly first.
The problem is when date-picker finished rendering, are the main vue datas ready?
It seems you pass a null into options, but select another month will force update options.
could you try this?
How to Initialize Data Properties with Prop Values
make a v-if="pickerOptions" in attr

How do I go about creating an always capitalized input in vue.js?

I'm creating a form using vue.js and I need to create inputs in vue that is always capitalized. I know I could use the css property
text-transform: uppercase;
and then transform the data before sending using
data.someData.toUpperCase()
But I wonder if there is a more intelligent way of doing that in vue.js. In react we can create controlled inputs and easily do it. Is there anything like that in Vue.js?
I managed to do it using computed fields, however, I would have to create computed getter and setter for each input in the form. Is there a better way of doing it?
You could create a custom directive.
Vue.directive( 'touppercase', {
update (el) {
el.value = el.value.toUpperCase()
},
})
And then use it where you need. For example:
<input type="text" v-model="modelfield" v-touppercase>
Since you don't have a lot of code to run, you should manually bind events to your textfield and then handle the uppercasing there.
Handling events from a text field can be done by adding an input event handler on them, and then updating the initial state again.
<input :value="text" #input="updateText($event.target.value)"/>
export default {
data() {
return {
text: '',
}
},
methods: {
updateText(newValue) {
this.value = newValue.toUpperCase();
},
}
}
You can also do it inline in a template, but this might make it harder to read depending on your code style preferences
<input :value="text" #input="text = $event.target.value.toUpperCase()"/>
This directive works fine with v-model (last character is in upper case too):
Vue.directive('uppercase', {
update(el) {
const sourceValue = el.value;
const newValue = sourceValue.toUpperCase();
if (sourceValue !== newValue) {
el.value = newValue;
el.dispatchEvent(new Event('input', { bubbles: true }));
}
},
});
Usage:
<input type="text" v-model="myField" v-uppercase />
You can do this:
<input :value="theValue" #input="theValue = theValue.toUpperCase()"/>
as a fix for asologor's answer you should reach input element to change it at vuetify
Vue.directive("uppercase", {
update(el) {
const sourceValue = el.getElementsByTagName("input")[0].value
const newValue = sourceValue.toUpperCase()
if (sourceValue !== newValue) {
el.getElementsByTagName("input")[0].value = newValue
el = el.getElementsByTagName("input")[0]
el.dispatchEvent(new Event("input", { bubbles: true }))
}
}
})
and usage is
<v-text-field
type="text"
outlined
placeholder="placeholder"
v-model="name"
prepend-inner-icon="edit"
v-uppercase
/>
this is defnetely working

How do i get property of an object instead of whole object on v-model in vue.js

I had created an input form using html and given v-model="upCountryName", which is an empty array by default but if once name value clicked I had written function to fetch the data from django db using djangorestframework and I had got the data also but in input from v-model="upCountryName" I am getting data as [object Object].
So I written v-model="upCountryName.country" which actually I want to update but input form showing empty, how can I get country name instead of whole object.
----------
HTML --
<td v-on:click="up_country_form(c.id)">
<!--c.id is country.id i am getting from django db and passing it to the method up_country_form(id) in script-->
<center>
<p class="fas fa-pencil-alt"></p>
</center>
</td>
<input type="text" v-model='upCountryName.country' class="form-control">
----------
Vue.js
<script>
export default {
data () {
return {
upCountryName: []
};
},
methods: {
up_country_form (id) {
this.up_country_box = true;
this.$http.get('http://127.0.0.1:8000/getCountry/'+id)
.then((response) => {
this.upCountryName = response.data;
this.loading = false;
});
console.log(id);
this.add_country_box = false;
}
}
};
</script>
You have to make sure response.data is in form like { country: "Some Country Name" } then everything should work fine.
You can see the demo here.
Note: if you use upCountryName.country, the type of upCountryName should be Object not Array.
data () {
return {
upCountryName: {}
}
}

Vue 2 custom select2: why is #change not working while #input is working

I created a custom select2 input element for Vue 2.
My question is: why is
<select2 v-model="vacancy.staff_member_id" #input="update(vacancy)"></select2>
working, but
<select2 v-model="vacancy.staff_member_id" #change="update(vacancy)"></select2>
not?
Since normal <input> elements in Vue have a #change handler, it would be nice if my custom select2 input has the same.
Some information on my custom element:
The purpose of this element is to not render all <option> elements but only those needed, because we have many select2 inputs on one page and many options inside a select2 input, causing page load to become slow.
This solution makes it much faster.
Vue.component('select2', {
props: ['options', 'value', 'placeholder', 'config', 'disabled'],
template: '<select><slot></slot></select>',
data: function() {
return {
newValue: null
}
},
mounted: function () {
var vm = this;
$.fn.select2.amd.require([
'select2/data/array',
'select2/utils'
], function (ArrayData, Utils) {
function CustomData ($element, options) {
CustomData.__super__.constructor.call(this, $element, options);
}
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.query = function (params, callback) {
if (params.term && params.term !== '') {
// search for term
var results;
var termLC = params.term.toLowerCase();
var length = termLC.length;
if (length < 3) {
// if only one or two characters, search for words in string that start with it
// the string starts with the term, or the term is used directly after a space
results = _.filter(vm.options, function(option){
return option.text.substr(0,length).toLowerCase() === termLC ||
_.includes(option.text.toLowerCase(), ' '+termLC.substr(0,2));
});
}
if (length > 2 || results.length < 2) {
// if more than two characters, or the previous search give less then 2 results
// look anywhere in the texts
results = _.filter(vm.options, function(option){
return _.includes(option.text.toLowerCase(), termLC);
});
}
callback({results: results});
} else {
callback({results: vm.options}); // no search input -> return all options to scroll through
}
};
var config = {
// dataAdapter for displaying all options when opening the input
// and for filtering when the user starts typing
dataAdapter: CustomData,
// only the selected value, needed for un-opened display
// we are not using all options because that might become slow if we have many select2 inputs
data:_.filter(vm.options, function(option){return option.id === parseInt(vm.value);}),
placeholder:vm.placeholder
};
for (var attr in vm.config) {
config[attr] = vm.config[attr];
}
if (vm.disabled) {
config.disabled = vm.disabled;
}
if (vm.placeholder && vm.placeholder !== '') {
$(vm.$el).append('<option></option>');
}
$(vm.$el)
// init select2
.select2(config)
.val(vm.value)
.trigger('change')
// prevent dropdown to open when clicking the unselect-cross
.on("select2:unselecting", function (e) {
$(this).val('').trigger('change');
e.preventDefault();
})
// emit event on change.
.on('change', function () {
var newValue = $(this).val();
if (newValue !== null) {
Vue.nextTick(function(){
vm.$emit('input', newValue);
});
}
})
});
},
watch: {
value: function (value, value2) {
if (value === null) return;
var isChanged = false;
if (_.isArray(value)) {
if (value.length !== value2.length) {
isChanged = true;
} else {
for (var i=0; i<value.length; i++) {
if (value[i] !== value2[i]) {
isChanged = true;
}
}
}
} else {
if (value !== value2) {
isChanged = true;
}
}
if (isChanged) {
var selectOptions = $(this.$el).find('option');
var selectOptionsIds = _.map(selectOptions, 'value');
if (! _.includes(selectOptionsIds, value)) {
var missingOption = _.find(this.options, {id: value});
var missingText = _.find(this.options, function(opt){
return opt.id === parseInt(value);
}).text;
$(this.$el).append('<option value='+value+'>'+missingText+'</option>');
}
// update value only if there is a real change
// (without checking isSame, we enter a loop)
$(this.$el).val(value).trigger('change');
}
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
The reason is because you are listening to events on a component <select2> and not an actual DOM node. Events on components will refer to the custom events emitted from within, unless you use the .native modifier.
Custom events are different from native DOM events: they do not bubble up the DOM tree, and cannot be captured unless you use the .native modifier. From the docs:
Note that Vue’s event system is separate from the browser’s EventTarget API. Though they work similarly, $on and $emit are not aliases for addEventListener and dispatchEvent.
If you look into the code you posted, you will see this at the end of it:
Vue.nextTick(function(){
vm.$emit('input', newValue);
});
This code emits a custom event input in the VueJS event namespace, and is not a native DOM event. This event will be captured by v-on:input or #input on your <select2> VueJS component. Conversely, since no change event is emitted using vm.$emit, the binding v-on:change will never be fired and hence the non-action you have observed.
Terry pointed out the reason, but actually you can simply pass your update event to the child component as a prop. Check demo below.
Vue.component('select2', {
template: '<select #change="change"><option value="value1">Value 1</option><option value="value2">Value 2</option></select>',
props: [ 'change' ]
})
new Vue({
el: '#app',
methods: {
onChange() {
console.log('on change');
}
}
});
<script src="https://unpkg.com/vue#2.4.2/dist/vue.min.js"></script>
<div id="app">
<div>
<p>custom select</p>
<select2 :change="onChange"></select2>
</div>
<div>
<p>default select</p>
<select #change="onChange">
<option value="value1">Value 1</option>
<option value="value2">Value 2</option>
</select>
</div>
</div>
fiddle

How to separate in vue.js entered value and displayed value in input[type=text]?

Example:
<span class='prefix'>+{{ prefix }}</span>
<input type='tel' v-model='phone'>
What should be displayed
When phone === '790012345678', it is actually
prefix = '7'
phone = '90012345678'
And displayed accordingly
<span class='prefix'>+7</span>
<input type='tel' value='90012345678'>
When user removes value from input, prefix is removed too.
Problem (jsfiddle http://jsfiddle.net/4qqza69k/48/)
I use watcher for `phone`.
When user changes something inside `input` watcher must update value for `phone`, but this way it is triggered again and it receives updated (incorrect) value.
Scenarios:
Phone equals 7-100-200-30-40
prefix = +7, phone = 1002003040
Phone equals 7
prefix = +7, phone = ''
Phone equals 7123
prefix = +7, phone = 123
Phone is empty
prefix = '', phone = ''
Problem: how to exclude prefix from input without triggering updates?
I think you need to rewrite v-model into more explicit v-on + v-bind pair and listen to input for a phone number, while calculating prefix and the rest part separately:
new Vue({
el: '#app',
data: {
prefix: '',
phone: '', // full phone number
},
methods: {
handleInput: function(e) {
if (e.target.value === '') this.prefix = '';
if (this.prefix !== '') {
this.phone = this.prefix + e.target.value;
} else {
const v = e.target.value;
this.phone = v;
this.prefix = v.slice(0,1);
}
}
},
computed: {
withoutPrefix: function() {
return (this.prefix !== '') ? this.phone.slice(1) : ''
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<span class='prefix'>+{{ prefix }}</span>
<input type='tel' #input="handleInput" :value="withoutPrefix">
<p>Phone: {{ phone }}</p>
</div>
It does not exactly work cause I'm being a bit confused by your example, but I think one way is to use computed getter/setter instead.
data() {
return {
hiddenInputValue: ''
}
},
computed: {
inputValue: {
get() {
return this.hiddenInputValue;
},
set(val) {
// do something with the input value...
this.hiddenInputValue = // assign a modified version of the input value...
}
}
}
You should be able to do something with that, but check my comment. It's probably a better solution.