Bootstrap vue input type="number" - vue.js

When creating type="number" input, it getting '+' or '-' characters. I want to prevent it. However when I tried to watch every state changed on input value, it does not show '+' or '-' characters.
input:
<b-form-input
v-model="port"
type="number"
>
and watch:
watch: {
port(val){
console.log("val ", val)
}
},
For example when I write '564+-' to the input then the watch just can follow '564'. How can prevent minus and plus characters?

You can achieve it by cancel the keyboard event for invalid input using keydown event.
Demo :
new Vue({
el: "#app",
data: {
port: "",
},
methods: {
portKeydown(e) {
if (/^\+$/.test(e.key)) {
e.preventDefault();
}
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="demo">
<div>
<div>With key handler:</div>
<input type="number" v-model="port" #keydown="portKeydown($event)" />
</div>
</div>
</div>

Related

Vue.js watcher not watching v-model changes

I am having a problem with vue.js.
I have it so when you add a new item it saves to local storage but i also want it to save to local storage when you edit the item on the input. I feel like it should be working because of the v-model but it doesn't.
<template>
<div class="hello">
<h1>TODO</h1>
<input type="text" name="" value="" placeholder="E.g do homework..." v-model="input">
<br>
<input type="submit" name="button" #click="add()">
<p></p>
<div class="" v-for="(item, i) in todo" v-bind:key="i">
<input type="text" v-model="item.content" />
</div>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data(){
return{
todo: [
{content:"welcome", done: false}
],
input: "",
}
},
methods: {
add(){
if (this.input.trim() === '' || this.input === null){
return
}else{
this.todo.push({
content: this.input,
done: false,
})
this.input = ""
}
}
},
watch:{
todo(newVal) {
localStorage.setItem("todo",JSON.stringify(newVal))
console.log(localStorage.getItem('todo'))
},
deep: true
},
mounted(){
this.todo = JSON.parse(localStorage.getItem('todo')) || []
}
}
</script>
I can't see why this doesn't work and I have tried going through the vue.js documentation and haven't found anything about this.
Any help is appreciated.
Thanks.
This is expected because when you are editing item.content from input you are mutating the object (and the array) and not replacing it. Vue watchers can't observe mutations. As explained here
You should probably create a new method and use javascript methods that returns a copy of your array and does not mutates/changes. More on here.

How do I display a value as a decimal place in vuejs input field?

I'm trying to display a 2000.00 in an input field in vuejs but it strips the .00.
<div id="app">
<input
class="form-control"
type="number"
v-model="solicitorsFees"/>
</div>
new Vue({
el: "#app",
data: {
solicitorsFees: 2000.00,
},
})
How do I get the input field to display 2000.00?
jsfiddle
I can do this with a calculated property. But I need to apply this to multiple input fields.
solicitorsFeesDecimal: function(){
return(this.solicitorsFees.toFixed(2))
},
<input class="form-control" type="number" v-model="solicitorsFeesDecimal"/>
Solution:
<input class="form-control" type="number" :value="(this.solicitorsFees).toFixed(2)"/>
The appropriate solution is to use a computed property with a custom getter and setter, as well as using the v-model.number modifier:
Template
<div id="app">
<input
class="form-control"
type="number"
v-model.number="solicitorsFeesDisplay"/>
</div>
Script
new Vue({
el: "#app",
computed: {
solicitorsFeesDisplay: {
get: function() {
return this.solicitorsFees.toFixed(2)
},
set: function(newValue) {
this.solicitorsFees = newValue
}
}
},
data() {
return {
solicitorsFees: 2000.00
}
},
})
See a working example on CodeSandbox.
you should you step in input field:
<input type="number" v-model="solicitorsFees" step="0.01">
This solves the problem:
<input class="form-control" type="number" :value="(this.solicitorsFees).toFixed(2)"/>
You can simply use parseFloat and toFixed together to define the number of decimal places you want.
v-model= parseFloat(solicitorsFees).toFixed(2)
Also, another suggestion is to make the data object as a function, as in your fiddle you are using an object.
data () {
return {
solicitorsFees: 2000.00
}
}

v-model input working after pressing enter

I am working in Vue
My input search bar is filtering after every letter that I type. I want it to filter after I pressed the enter key.
Can somebody help me please?
<template>
<div id="show-blogs">
<h1>All Blog Articles</h1>
<input type="text" v-model="search" placeholder="Find Car" />
<div v-for="blog in filteredBlogs" :key="blog.id" class="single-blog">
<h2>{{blog.title | to-uppercase}}</h2>
<article>{{blog.body}}</article>
</div>
</div>
</template>
<script>
export default {
data() {
return {
blogs: "",
search: ""
};
},
methods: {},
created() {
this.$http
.get("https://jsonplaceholder.typicode.com/posts")
.then(function(data) {
// eslint-disable-next-line
console.log(data);
this.blogs = data.body.slice(0, 10);
});
},
computed: {
filteredBlogs: function() {
return this.blogs.filter(blog => {
return blog.title.match(this.search);
});
}
}
};
</script>
There are a few ways you could accomplish this. Probably the most accessible would be to wrap the input in a form and then user the submit event to track the value you want to search for. Here's an example:
<template>
<div id="show-blogs">
<h1>All Blog Articles</h1>
<form #submit.prevent="onSubmit">
<input v-model="search" type="text" placeholder="Find Car" />
</form>
</div>
</template>
export default {
data() {
return {
search: '',
blogSearch: '',
};
},
computed: {
filteredBlogs() {
return this.blogs.filter(blog => {
return blog.title.match(this.blogSearch);
});
},
},
methods: {
onSubmit() {
this.blogSearch = this.search;
},
},
};
Notice that blogSearch will only be set once the form has been submitted (e.g. enter pressed inside the input).
Other notes:
You'll probably want to trim your search value
You should add a label to your input.
You could skip using v-model and instead add a keyup event handler with the .enter modifier that sets the search data property
<input type="text" :value="search" placeholder="Find Car"
#keyup.enter="search = $event.target.value" />
Demo...
new Vue({
el: '#app',
data: () => ({ search: '' })
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<input type="text" :value="search" placeholder="Find Car"
#keyup.enter="search = $event.target.value" />
<pre>search = {{ search }}</pre>
</div>

Apply v-focus to the first input field on a page

I've a Vue component in which I'm trying to autofocus the first field using v-focus. But my problem is, I've dynamic components that will be included at the top of the page. So in that case how can I apply autofocus to dynamically included component?
They key is to set ref on all your inputs to the same string like this:
<input type="text" ref="myInputs"/>
Then you will have access to an array called this.$refs.myInputs inside an event handler.
So you just need to do
this.$refs.myInputs[0].focus();
new Vue({
el: "#app",
mounted() {
this.$refs.myInputs[0].focus();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div>
<div v-for="index in 3" :key="index">
<input ref="myInputs" type="text" />
</div>
</div>
</div>
It's hard to tell how you're adding the input(s) to the DOM, without any pseudo code from you, but this is one way to do it..
[CodePen mirror]
new Vue({
el: "#app",
data: {
inputs: ["firstName", "lastName"]
},
watch: {
inputs() {
this.$nextTick(() => {
this.focusFirstInput();
});
}
},
methods: {
focusFirstInput() {
let first = this.inputs[0];
let firstInput = this.$refs[first][0];
firstInput.focus();
},
handleClick() {
this.inputs.push("newInput");
}
},
mounted() {
this.focusFirstInput();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div>
<div v-for="(input, index) in inputs" :key="index">
<input :ref="input" type="text" />
</div>
<div>
<button type="button" #click="handleClick">Click to add input</button>
</div>
</div>
</div>
I found this answer on Laracast and it worked for me. All I did was insert the code below in my dynamic form field.
this.$nextTick(() => {
let index = this.items.length - 1;
let input = this.$refs.title[index];
input.focus();
});
HTML
<div id="app">
<ul v-for="item in items">
<li>
<input :ref="'title'" v-model="item.title">
</li>
</ul>
<button v-on:click="addItem">Add Item</button>
</div>
JS
let app = new Vue({
el: '#app',
data: {
items: [
{title: 'Apple'},
{title: 'Orange'},
]
},
methods: {
addItem(){
this.items.push({title: "Pineapple"});
this.$nextTick(() => {
let index = this.items.length - 1;
let input = this.$refs.title[index];
input.focus();
});
}
}
});
Note: make sure to add :ref="'title'" into your dynamic form field.
Credits to the original author of the solution.

custom v-focus not work when there are many v-if in parent node

I define the custom directive "focus" in my component:
<script>
export default {
name: 'demo',
data () {
return {
show: true
}
},
methods: {
showInput () {
this.show = false
}
},
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}
}
And this is my html template:
<template>
<div>
<input type="number" id="readonly" v-if="show">
<button type="button" #click="showInput" v-if="show">show</button>
<input type="number" id="timing" v-model="timing" v-if="!show" v-focus>
</div>
</template>
But when I click the button, input#timing can't autofocus.
When I put input#readonly and button into a div and use only one v-if, input#timing can be autofocus:
<template>
<div>
<div v-if="show">
<input type="number" id="readonly">
<button type="button" #click="showInput">show</button>
</div>
<input type="number" id="timing" v-model="timing" v-if="!show" v-focus>
</div>
</template>
This is why???
The directive's code is indeed running and focusing the <input>.
But it is being removed from the DOM! When this happens, it loses focus. Check the console of the fiddle below: https://jsfiddle.net/acdcjunior/srfse9oe/21/
Another important point is that, when inserted is called, the <input id="timing"> is in the DOM (as mentioned above), but it is in the DOM at the wrong location (between <p>a</p> and <p>b</p> where it was never supposed to be). This happens because Vue tries to reuse elements.
And when the nextTick triggers (see fiddle), it is in its correct placement (between <p>c</p> and <p>d</p>), because Vue moved it to the correct location. And is this moving that is taking focus out.
And because nextTick runs after the DOM moving around is done, the focus persists (see below).
Using Vue.nextTick():
Defer the callback to be executed after the next DOM update cycle. Use
it immediately after you’ve changed some data to wait for the DOM
update.
new Vue({
el: '#app',
data() {
return {
show: true,
timing: 123
}
},
methods: {
showInput() {
this.show = false
}
},
directives: {
focus: {
inserted: function(el) {
Vue.nextTick(() => el.focus()); // <======== changed this line
}
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div>
<input type="number" id="readonly" v-if="show">
<button type="button" #click="showInput" v-if="show">show</button>
<input type="number" id="timing" v-model="timing" v-if="!show" v-focus>
</div>
</div>