Is there specific number input component in Vuetify? - javascript

I've seen a component in Element UI for managing the amount of items, it's over here:
https://element.eleme.io/#/en-US/component/input-number
I would want to use something like that in Vuetify, but I cannot find a similar component or even similar style example in Material Design. What's the best way to achieve it?

Yes there is:
<v-text-field
v-model="numberValue"
hide-details
single-line
type="number"
/>
Check out slider component docs for a working example.

Update: This answer pertains to version 1 of Vuetify, yukashima huksay's answer is correct for newer versions of Vuetify.
Setting the type attribute to type="number" is the way to go.
Original:
You could just make your own:
new Vue({
el: '#app',
data () {
return {
foo: 0
}
},
methods: {
increment () {
this.foo = parseInt(this.foo,10) + 1
},
decrement () {
this.foo = parseInt(this.foo,10) - 1
}
}
})
<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons' rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-content>
<v-container>
<v-text-field v-model="foo" type="number" label="Number" append-outer-icon="add" #click:append-outer="increment" prepend-icon="remove" #click:prepend="decrement"></v-text-field>
</v-container>
</v-content>
</v-app>
</div>

in vuetify.js v2.2.22 to convert your <v-text-field> in number you need write v-model.number
<v-text-field
v-model.number="foo"
label="Number"
append-outer-icon="add"
#click:append-outer="increment"
prepend-icon="remove"
#click:prepend="decrement">
</v-text-field>
type="number" was delete

Some concepts for number inputs get mixed up here.
I can not see type="number" being deleted in 2.2.22 https://github.com/vuetifyjs/vuetify/compare/v2.2.21...v2.2.22 Also I see it being rendered correctly at least in 2.3.10
The input field with attribute type="number" will be handled differently depending on the browser, OS and locale settings (e.g. I am still able to input free text in FF but not Chrome). Typically the keyboard layout changes on smart phones.
v-model.number is purely a directive for Vue. As you can see, internally, Vue simply tries to parse the input with parseFloat and uses that on success - otherwise it will be text and handled as a string in Vue/JS. https://v2.vuejs.org/v2/guide/forms.html#number

Vue vuetify Code
using :rules="maxRules"
<template>
<div>
<v-text-field v-model="text1" :rules="maxRules" label="Credit Amount"></v-text-field>
</div>
</template>
<script>
export default {
data () {
return {
limit:500,
maxRules: [
(v)=> {
if (this.text1 > this.limit) {
return 'Error'
}
}
]
}
}
}
</script>

Related

Vue 3 v-model not properly updating on in Andoid's Chrome?

I have a component, which is essentially an input/select hybrid field, which allows users to type in the input field, and select items from the dropdown, based on their query.
It works perfectly fine on most devices I've tried, i.e. as the user types something into the input field, the list of items updates and only shows those items which contain that piece of string.
Except the Chrome browser on my Android device - as you type, the list doesn't seem to update, unless I press the "space bar". Very strange. Anyone have any ideas why this might be?
Here is the code in <script setup>:
const props = defineProps([ 'items', 'searchBy' ])
const searchTerm = ref('')
const itemsToShow = computed(() => {
if (props.items) {
if (searchTerm.value) {
return props.items.filter(el => {
if (props.searchBy) {
return el[props.searchBy].toUpperCase().indexOf(searchTerm.value.toUpperCase()) > -1
}
return el.toUpperCase().indexOf(searchTerm.value.toUpperCase()) > -1
})
} else {
return props.items
}
} else {
return []
}
})
And the HTML:
<input
type="text"
class="input"
v-model="searchTerm"
placeholder=" "
/>
<div class="items-list">
<div
v-for="item in itemsToShow"
:key="item"
#click="() => handleAdd(item)"
class="item text"
>
{{ item }}
</div>
<div
v-if="!itemsToShow.length"
class="text"
>
No items match searched term
</div>
</div>
UPDATE:
I've investigated a little, and it seems the searchTerm ref, isn't updating properly, even though its bound using v-model... Still no idea why though.
I've ran into this issue before.
It seems that on certain devices, the v-model waits for a change event, instead of an input one.
Apparently, it's to do with the input method editor (IME) for the specific device.
You can check a discussion about this at https://github.com/vuejs/core/issues/5580
The workaround is to simply bind the input field with value and listen for the input event manually, e.g.
<input
type="text"
class="input"
:value="searchTerm"
#input="(e) => searchTerm = e.target.value"
placeholder=" "
/>

Vue.js3 event #keydown.esc on text input is not triggered

I have a text field with various events. All events are working, but not #keydown.esc.
I would like to differ between #blur and #keydown.esc. Escape should always do something and #blur only sometimes.
Found this code codepen.io/whitelynx/pen/wGvmdW
that shows the key code for every key, but not for escape.
Is there anything special with #keydown.esc in vue js 3? That was not an issue in version 2.
My element
<input type="text" v-model="searchQuery" #keyup="searchItems" #paste="searchItems" #keydown.down="arrowDown" #keydown.up="arrowUp" #keydown.enter="selectIndex"
#keyup.,="selectIndex" #keydown.esc="closeSearch" #blur="maybeCloseSearch"
#focus="initSearch" placeholder="Search …" autocomplete="off" \>
I tried to remove all the other events and only #keydown.esc, but that makes no difference
"Solution": I found out that my browser extension Vimium i causes this problem
Fix the error:
wrong end of the input tag
\>
Define all methods
It works!
Playground
const App = {
data() { return { searchQuery: null } },
methods: {
initSearch: () => console.log('initSearch'),
closeSearch: () => console.log('closeSearch'),
maybeCloseSearch: () => console.log('maybeCloseSearch'),
}
}
const app = Vue.createApp(App);
app.mount('#app');
#app { line-height: 2; }
[v-cloak] { display: none; }
<div id="app">
<input type="text" v-model="searchQuery" #keydown.esc="closeSearch" #blur="maybeCloseSearch" #focus="initSearch" placeholder="Search …" autocomplete="off" />
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>

Getting multiple selected value from v-chip-groups

there are two arrays
categoryArr=['test','demo'] and regionArr = ['GJ','MH']
i am using v-chip-groups to display this array values in chips
<v-row>
<v-chip-group
class="float-right"
multiple
v-model="rgSelected"
active-class="deep-purple--text text--accent-2"
>
<v-chip filter v-for="(region,i) in regionArr" :value="region" :key="i">{{region}}</v-chip>
</v-chip-group>
</v-row>
<v-divider class="my-2"></v-divider>
<span style="font-size:1.5rem">Category</span>
<br />
<v-row>
<v-chip-group
class="float-right"
multiple
v-model="ctSelected"
active-class="deep-purple--text text--accent-2"
>
<v-chip filter v-for="(category,i) in categoryArr" :value="category" :key="i">{{category}}</v-chip>
</v-chip-group>
</v-row>
I'm using watch here
watch: {
rgSelected: "showDuedate",
ctSelected: "showDuedate"
}
methods: {
showDuedate() {
console.log(this.ctSelected, this.rgSelected);
}
},
and the v-model are ctSelected =[] and rgSelected = [].
But when i click on 'GJ' the rgSelected = ['GJ']
and after that if i click on 'test' the output displays ctSelected=['GJ','test'] and rgSelected=['GJ'].
Can you tell me what I'm doing wrong here.
I checked it here on mine and there are no errors and all was fine. Seems vuetify version issue, please upgrade it. I solve it after upgrading.
here's the result.
ctSelected: -- rgSelected : GJ
ctSelected: test -- rgSelected : GJ
ctSelected: test,demo -- rgSelected : GJ
the v-chips

Vue component as child of another component

I am working on converting an existing theme into reusable components.
I currently have a button component like so:
<template>
<a :href="link" class="button" :class="styling"><slot></slot></a>
</template>
<script>
export default {
props: {
link: {},
styling: {
default: ''
}
}
}
</script>
And, in my HTML, I use it like so:
<vue-button link="#" styling="tiny bg-aqua">Button 1</vue-button>
Now, I am attempting to create a "button group" using the existing button component.
What I would like to be able to do is something like this:
<vue-button-group styling="radius tiny">
<vue-button link="#" styling="tiny bg-aqua">Button 1</vue-button>
<vue-button link="#" styling="tiny bg-aqua">Button 2</vue-button>
<vue-button link="#" styling="tiny bg-aqua">Button 3</vue-button>
<vue-button link="#" styling="tiny bg-aqua">Button 4</vue-button>
</vue-button-group>
I am very new to VueJS, and am a bit confused on the proper way to handle such a thing. I would like to be able to pass as many button components into the group as is needed.
Here is what I have so far for the button group:
<template>
<ul class="button-group" :class="styling">
<li><slot></slot></li>
</ul>
</template>
<script>
export default {
props: {
styling: {
default: ''
}
}
}
</script>
This would, of course, work with a single button being passed in, but I cannot seem to figure out how to allow for more than that, while encasing each button within its own list item.
Any suggestions or corrections to the way I am going about this would be very much appreciated. Thank you.
Since you want to do advanced things with the output of your component, this might be the time to go to render functions, as they allow far more flexibility:
Vue.component('button-group', {
props: {
styling: {
default: ''
}
},
render(createElement) { // createElement is usually called `h`
// You can also do this in 1 line, but that is more complex to explain...
// const children = this.$slots.default.filter(slot => slot.tag).map(slot => createElement('li', {}, [slot]))
const children = [];
for(let i = 0; i < this.$slots.default.length; i++) {
if(!this.$slots.default[i].tag) {
// Filter out "text" nodes, so we don't make li elements
// for the enters between the buttons
continue;
}
children.push(createElement('li', {}, [
this.$slots.default[i]
]));
}
return createElement('ul', {staticClass: "button-group",class: this.styling}, children);
}
})
var app = new Vue({
el: '#app',
})
.rainbow-background {
/* todo: implement rainbow background */
border: red 1px solid;
}
.button-group {
border-color: blue;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<button-group styling="rainbow-background">
<button>hi</button>
<button>How are you?</button>
<button>I'm fine</button>
<button>Have a nice day!</button>
</button-group>
</div>
A render function works by returning an virtual html structure, this structure is generated by repeated calls to a createElement functions. createElement accepts 3 parameters, a tag name (like ul or li), an options object, and an list of children.
We first start by generating a list of children with our incoming slots, who are stored inside this.$slots.default.
Then we loop over this list, filtering out incoming slot data that is basically text, this is because the way HTML considers whitespace between tags as text.
We are now almost complete with our final structure, we now wrap the slot element in a freshly generated li tag, and then we finish the generating of with wrapping everything in a new ul tag with the proper class names.
The Vue way to do this is use names slots, and provide the data the child uses to the parent. The data will propagate to the child via the slot-scope.
The whole key to this is the data flows through the parent to the child.
Here's a working codepen of the process: https://codepen.io/Flamenco/pen/ZMOdYz
This example uses the default slot, so name attribute is not needed on parent or child.
Parent
<template>
<ul class="button-group" :class="styling">
<li v-for='item in items'><slot :item='item'></slot></li>
</ul>
</template>
<script>
...
props:{
items: Array
}
</script>
Child
<vue-button-group class="radius tiny" :items='items'>
<template slot-scope='scope'>
<vue-button link="#" styling="tiny bg-aqua">{{scope.item.text}}</vue-button>
</template>
</vue-button-group>
<script>
...
data:{
items:[{text:'Button 1'},{text:'Button 2'},{text:'Button 3}]
}
</script>
You can also try to use a named slot
<template>
<ul class="button-group">
<li><slot name="buttons"></slot></li>
</ul>
</template>
And than:
<vue-button-group class="radius tiny">
<template slot="buttons">
<vue-button link="#" styling="tiny bg-aqua">Button 1</vue-button>
<vue-button link="#" styling="tiny bg-aqua">Button 2</vue-button>
<vue-button link="#" styling="tiny bg-aqua">Button 3</vue-button>
<vue-button link="#" styling="tiny bg-aqua">Button 4</vue-button>
</template>
</vue-button-group>
A possible solution for that is to use v-for.
<button v-for="button in buttons" :key="button">
Button{{button}}
</button>
Here is the fiddle.
From there you could build your <buttongroup>-component yourself; passing "meta-infos" into your <buttongroup> as props (in my fiddle the array in the data-part).
Slots would make sense, if you want to render something else besides the buttons and you want to inject components for that. Otherwise you would gain nothing with slots.

Vue.JS 2.0 slow when modifying unrelated data

Suppose I have an input field in Vue.JS that v-model bind to a String data property, and a long list of random numbers that are completely unrelated to that first String.
data: {
input: "",
randoms: []
}
<input type="text" v-model="input">
<p v-for="random in randoms" v-text="random"></p>
When I put both in the same Vue, I see a huge slowdown when typing in the input field, as it appears Vue is reevaluating the DOM for each list entry after every input event, although they really have nothing to do with each other.
https://jsfiddle.net/5jf3fmb8/2/
When I however move the v-for to a child component where I bind randoms to a prop, I experience no such slowdown
https://jsfiddle.net/j601cja8/1/
Is there a way I can achieve the performance of the second fiddle without using a child-component?
Is there a way I can achieve the performance of the second fiddle without using a child-component?
Short answer
No.
Long answer
Whenever any dependency of the template changes, Vue has to re-run the render function for the entire component and diff the new virtualDOM against the new one. It can't do this for this or that part of the template only, and skip the rest. Therefore, each time the input value changes, the entire virutalDOM is re-rendered.
Since your v-for is producing quite a bit of elements, this can take a few 100ms, enough to be noticable when you type.
Extracting the heavy part of the template into its own component is in fact the "official" way to optimize that.
As Alex explained, v-model.lazy might improve the situation a bit, but does not fix the core of the issue.
Shortest, simplest answer: change v-model to v-model.lazy.
When I put both in the same Vue, I see a huge slowdown when typing in the input field, as it appears Vue is reevaluating the DOM for each list entry after every input event, although they really have nothing to do with each other.
Note that the OnceFor sample still chugs like mad despite not actually being reactive any more. I don't understand Vue well enough to say if that's intentional or not.
const Example = {
data() { return { input: "", randoms: [] } },
created() { this.newRandoms() },
methods: {
newRandoms() { this.randoms = Array(50000).fill().map(() => Math.random()) }
}
}
new Vue({
el: "#vue-root",
data(){ return {example: 'lazy-model'}},
components: {
LazyModel: {...Example, template: "#lazy-model"
},
OnceFor: {...Example, template: "#once-for"
},
InlineTemplate: {...Example, template: "#inline-template",
components: {
Welp: {
props: ['randoms']
}
}
}
}
})
button,
input,
div {
margin: 2px;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="vue-root">
<span><button v-for="(component, name) in $options.components" #click="$set($data, 'example', name)">{{name}}</button></span>
<component :is="example"></component>
</div>
<template id="lazy-model">
<div>
<input type="text" v-model.lazy="input"><br>
<input type="submit" value="Regenerate" #click="newRandoms">
<p v-for="random of randoms" v-text="random"></p>
</div>
</template>
<template id="once-for">
<div>
<input type="text" v-model="input"><br>
<input type="submit" value="Regenerate" #click="newRandoms">
<p v-for="random of randoms" v-text="random" v-once></p>
</div>
</template>
<template id="inline-template">
<div>
<input type="text" v-model="input"><br>
<input type="submit" value="Regenerate" #click="newRandoms">
<welp :randoms="randoms" inline-template>
<div>
<p v-for="(random, index) of randoms" :key="index"> {{index}}: {{random}} </p>
</div>
</welp>
</div>
</template>

Categories

Resources