How to prevent Vue input from setting value? - vuejs2

I have the following Vue code:
// HTML
<div id="app">
Value: <pre>{{ value }}</pre>
<input type="text" :value="value" #input="setValue">
</div>
// JS
new Vue({
el: "#app",
data: {
value: '',
},
methods: {
setValue(event){
/* Remove non-numeric values */
this.value = event.target.value.replace(/[^\d]/g, '');
}
}
});
I have it set up on JSFiddle here: http://jsfiddle.net/eywraw8t/353729/.
Why does the input allow me to enter non-numeric values?
If you run the code above, and enter non-numeric gibberish into the input element (e.g. asdasfa), you'll see that the input element will contain your entered text (asdasfa), but the element above the input will be empty!
I would like to restrict users to only being allowed to enter numbers into the input. I would like to do this using only Vue, no 3rd party libraries or type="number".

because the value of this.value doesn't change (always ='') so it will not trigger re-render.
The solution:
you can use this.$forceUpdate() to force re-render.
or use bind key with different value.
new Vue({
el: "#app",
data: {
value: '',
errorDescription: ''
},
methods: {
setValue(event){
/* Remove non-numeric values */
this.value = event.target.value.replace(/[^\d]/g, '')
this.errorDescription = 'Only Number allow'
this.$forceUpdate()
}
}
})
.error {
background-color:red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
Value: <pre>{{ value }}</pre><span class="error">{{errorDescription}}</span>
<input type="text" :value="value" #input="setValue">
</div>

The issue is Vue doesn't see a change to your value data property because when you filter out non-numbers, you are essentially assigning the same string value back to it. Since strings are immutable and identical when their contents are the same, this doesn't trigger Vue's reactivity.
An easy solution is to manually set the <input> value to the new, number-only value.
this.value = event.target.value = event.target.value.replace(/\D/g, '')
http://jsfiddle.net/9o2tu3b0/

Related

How can I defined as Default checked in a checkbox with Vuejs

I have tried all that I have read in other questions and they did not answer me the problem.
I have a checkbox like this in a list
<input type="checkbox" v-model="selected" :value="post.rut">
I have the model defined like this:
data: function() {
return {
selected: []
}
}
The problem is that If I add checked as default.. it does not change at all I mean it keeps the checkbox not checked
<input type="checkbox" v-model="selected" :value="post.rut" checked>
If I remove the v-model, it works BUT I can not send the value to the controller because I need the v-model to bind so I wonder how can I set as default checked in that checkbox input like that?
Thanks
you have the input value as post.rut you can put it in selected array in data like :
data: function() {
return {
selected: [this.post.rut]
}
}
If you already know which field you want to selected based on the post you can do like so:
data: function() {
return {
selected: [this.post.rut]
Here is a working example of what you asked for.
Note: You need not use v-model and :value at the same time since v-model itself is a two way binder.
var app = new Vue({
el: '#app',
data() {
return {
selected: true
};
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="checkbox" v-model="selected"/>
</div>

Hide one item from input list that is dynamically generated unless other input from list has value in vue / vuetify

So the process is like this. I have a form that is created from api. I want to show all the inputs except for one. That one will only be shown if the user adds value to another specific input from the form.
Something like this is the idea.
<Form>
<div-for="formItem in state.formItemData">
<template v-if="formItem.one !== ''">
<form-input
v-model="invoiceForm.two"
:key="formItem.id"
ref="two"
></form-input>
</template>
</div>
</Form>
const invoiceForm = computed({
get: () => state.forms.formData,
set: (value) => {
state.forms.formData= value
}
})
The concept in your problem is very simple, you just must make use of a computed property that evaluates if there are values in input 1 and it will rerender the component showing input2.
new Vue({
el: '#app',
data: {
input1: '',
input2: ''
},
methods: {
verification() {
console.log(this.input1);
}
},
computed: {
notEmpty() {
return this.input1 !== '' && this.input1.length > 3;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input #change="verification" type="text" v-model="input1"> <input v-if="notEmpty" type="text" v-model="input2">
</div>
You should use a v-show if you're eventually going to show/hide it when rendering, especially if it's going to be multiple times. The Vue documentation says that the v-if will not be rendered when the initial condition is false.
Source: https://v3.vuejs.org/guide/conditional.html#v-if-vs-v-show

vue.js v-model on input can not differentiate between empty input and invalid input

I am trying to validate some input data (e.g. number) on an input-tag connected to some data via v-model.
The problem is, if I have invalid data (e.g. "1e"), the data will be "". Same goes obviously for empty input.
How can I differentiates empty input or invalid input?
var app = new Vue({
el: "#app",
data: {
budget: "",
},
methods: {
updateBudget() {
// do some input validation here. E.g.
if (this.budget === "") {
console.log("This is triggered on both:");
console.log("(1) empty input -> budget = ''");
console.log("(2) invalid input, e.g. -> budget = '1e'");
console.log("Problem: I can't split this in the above cases!");
};
},
},
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12/dist/vue.js"></script>
<div id="app">
<input v-model="budget" type="number" placeholder="Budget" min="0" step="0.01" #input="updateBudget" />
</div>
I'd appreciate a hint. Thanks
1e is not a number (also +, -). The output is empty as it's not a valid number, try to insert 1e0 or -1, you'll be able to catch the value. There are some related issues:
It is exactly how the spec says.
How to prevent extra characters
To catch the exact number without worry about 'e' use the internal Vue implementation. It takes care of such situations. (v-model.number)
<input
v-model.number="budget"
type="number"
placeholder="Budget"
min="0"
step="0.01"
#input="updateBudget"
/>
By the way, if you want to check 'e' explicitly, I think, it's better to use 'type=text'.
I did a custom solution based on some regex. Something like:
<template>
<div id="app">
<input v-model="budget" type="text" #input="validateAndUpdateInput" />
</div>
</template>
<script>
[...]
methods: {
validateAndUpdateInput: function() {
if (!/^-?([0]?|[1-9]{1}\d{0,15})([.]{1}\d{0,2})?$/.test(this.budget)) {
this.budget = this.previousInput;
//alert("not valid");
} else {
this.previousInput = this.budget;
this.$emit("update-input", this.budget);
}
},
[...]
</script>

Vuejs - Object notation for refs

I want to group my refs in to an object and access them by object notation. With that in mind, I now have this component:
new Vue({
el: '#app',
template: `
<div>
<input ref="input.name" type="text"/>
<input ref="input.email" type="email"/>
<input ref="input.password" type="password"/>
</div>
`,
created(){
console.log(this.$refs["input.name"]); // This works fine
console.log(this.$refs.input.name); // throws "TypeError: Cannot read property 'name' of undefined"
}
})
But it only shows this:
{
input.name: input,
input.email: input,
input.password: input,
}
What I want is something like this:
{
input: {
name: input,
email: input,
password: input,
},
}
Check fiddle here
Update:
I'm well aware of v-model but that's not what I need, I'll be doing something else with DOM.
new Vue({
el: '#app',
data() {
return {
inputs: {}
}
},
template: `
<div>
<input ref="input.name" type="text" value="123"/>
<input ref="input.email" type="email"/>
<input ref="input.password" type="password"/>
<button type="button" #click="clickMe">Click ME</button>
</div>
`,
mounted() {
this.setRefs();
console.log("this.inputs", this.inputs);
},
methods: {
setRefs() {
const INPUTS = {};
for (key in this.$refs) {
if (key.indexOf(".")) {
const KEYS_LIST = key.split(".");
INPUTS[KEYS_LIST[0]] = INPUTS[KEYS_LIST[0]] || {};
INPUTS[KEYS_LIST[0]][KEYS_LIST[1]] = this.$refs[key];
}
}
this.inputs = INPUTS;
},
clickMe() {
console.log("this.inputs.input.name.value", this.inputs.input.name.value);
},
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
This cannot work: refs only support strings as keys (see the docs), so there is not way of using objects here.
Also, a string with "dot notation" does not magically transform into an object with a nested property -- it's just a string. You could work around this by manually parsing the ref strings into an object structure, as has been demonstrated by #Ilia Brykin's answer. But I honestly think that you are misunderstanding the purpose of the whole ref system if you really want to do that.
P.S.: If you drop the "dort notation" part, you could use keys like input_name and access them via this.$refs.input_name.
EDIT
Another idea: you can get all input refs by their common prefix.
Object.keys(this.$refs).filter(ref => ref.startsWith('input.')
This gives you an array of ref strings of input elements.

Make two inputs update each other in two-way binding

I am trying to have two inputs update each other
Scenarios:
When I type in Base-10 number in field-1, field-2 must give Base-2 equivalent number
When I type in Base-2 number in field-2, field-1 must give Base-10 equivalent number
this is just an hypothetical scenario. I will need to deal with problems like this. I have provided an even more rudimentary example in my fiddle.
This should happen in this way:
When I make changes in Input-field-1, Input-field-2 value must change according to appropriate calculation.
When I make changes in Input-field-2, Input-field-1 value must change according to appropriate calculation.
Problem:
Right now, when I change Input-field-1, value of Input-field-2 changes which produces a warning as the change in Input-field-2 is trying to change value in Input-field-1... and I guess the process continues indefinitely.
This problem works without much issues in the fiddle because Vue.js has an awesome compiler. But I'd really like to know a better way to solve this. I tried isolating the variables from the function as much as possible.
My code-snippet:
HTML:
<div id="app">
<div>
I have <input v-model="perc" style="width:3em; text-align:right; background-color:#ffffe0;" v-on:change="amtCal">% of ${{total}}<strong> which is $</strong> <input v-model="amt" style="width:3em; text-align:right; background-color:#ffffe0;" v-on:change="percCal">
</div>
</div>
JS:
new Vue({
el: '#app',
data: {
total:'200',
perc:'10',
amt:''
},
computed:{
amtCal:function(){
this.amt=this.perc/100*this.total
return 0;
},
percCal:function(){
this.perc=this.amt/this.total*100;
return 0;
}
}
})
new Vue({
el: '#app',
data: {
total: '200',
perc: '10',
amt: ''
},
computed: {
amtCal: function() {
this.amt = this.perc / 100 * this.total
return 0;
},
percCal: function() {
this.perc = this.amt / this.total * 100;
return 0;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
I have <input v-model="perc" style="width:3em; text-align:right; background-color:#ffffe0;" v-on:change="amtCal">% of ${{total}}<strong> which is $</strong> <input v-model="amt" style="width:3em; text-align:right; background-color:#ffffe0;" v-on:change="percCal">
</div>
</div>
What I realize:
I've used the v-on:change property to execute a function when the value changes.
To keep the input from overwhelming the function executions, I used v-model.lazy in some other tests.
However, no matter what I do, it seems this problem will not subside as something fundamental is wrong with my approach.
There must be a better way.
I think I'd use a computed property with get and set.
https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter
I've glossed over validation, etc. below but it shows the basic principle of using one data value as the definitive source of truth while the other base uses a computed property.
new Vue({
el: '#app',
data () {
return {
num10: '6'
}
},
computed: {
num2: {
get () {
return Number(this.num10).toString(2)
},
set (num) {
this.num10 = parseInt(num, 2).toString()
}
}
}
})
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="app">
<div>
<label>Base 10 <input v-model="num10"></label>
</div>
<div>
<label>Base 2 <input v-model="num2"></label>
</div>
</div>