I use vue-select for multiple values.
Here is an example: https://codepen.io/sagalbot/pen/opMGro
I have the following code:
<v-select multiple v-model="selected" :options="options"></v-select>
And JS:
Vue.component('v-select', VueSelect.VueSelect)
new Vue({
el: '#app',
data: {
selected: ['foo','bar'],
options: ['foo','bar','baz']
}
Thank you!
You can use the v-on:input listener to see how many items are selected.
Then pass it a simple function as:
<v-select multiple v-model="selected" :options="options" v-on:input="limiter"></v-select>
After this, create a function called limiter in your methods and you'll get the current list of selected inputs,as
methods: {
limiter(e) {
if(e.length > 2) {
console.log(' you can only select two', e)
e.pop()
}
},
}
Now, if you add more than 2 items then the last one will be remove and you will see the console log
You can simply use inline condition:
<v-select multiple v-model="selected" :options="selected.length < 2 ? options: []">
I have limited to 2 options in the example above. The options will not be generated if there are 2 options selected. Remove the selected one and then you'll see the options dropdown.
Here's the updated demo.
you can use
:selectable="() => selected.length < 3"
from the official documentation https://vue-select.org/guide/selectable.html#limiting-the-number-of-selections
Related
I want to console.log visible items in table but #current-items emitter not working, please help me to figure out why?
I have vuetify: "1.3.11"
methods: {
currentItems(val) {
console.log(val) // the method is not called for some reason
this.test = val
console.log(this.test)
}
}
<v-data-table
#input="updateSelected"
v-bind="calculatedTableProps"
ref="dataTable"
:pagination.sync="localPagination"
:value="selected"
:headers="localHeaders"
:items="filteredItems"
:headers-length="headerCount"
:total-items="totalCount"
:loading="tableLoading"
class="big-data-table"
:class="tableFitContent ? 'table-fit-content' : ''"
:hide-actions="customActions"
v-scroll:[scrollTarget]="onScroll"
#current-items="currentItems"
>
The #current-items event does not seem to exist in Vuetify 1.x. Take a look at the docs of the v-data-table for Vuetify 1.5.24.
I'm writing a function to update a custom checkbox when clicked (and I don't want to use native checkbox for some reasons).
The code for checkbox is
<div class="tick-box" :class="{ tick: isTicked }" #click="() => isTicked = !isTicked"></div>
which works find.
However, there are so many checkboxes, so I use object to keep track for each item. It looks like this
<!-- (inside v-for) -->
<div class="tick-box" :class="{ tick: isTicked['lyr'+layer.lyr_id] }" #click="() => {
isTicked['lyr'+layer.lyr_id] = !isTicked['lyr'+layer.lyr_id]
}"></div>
Now nothing happens, no error at all.
When I want to see isTicked value with {{ isTicked }}, it's just shows {}.
This is what I define in the <script></script> part.
export default {
data() {
return {
isTicked: {},
...
};
},
...
}
Could you help me where I get it wrong?
Thanks!
Edit:
I know that declaring as isTicked: {}, the first few clicks won't do anything because its proerty is undefined. However, it should be defined by the first/second click not something like this.
Objects does not reflect the changes when updated like this.
You should use $set to set object properties in order to make them reactive.
Try as below
<div class="tick-box" :class="{ tick: isTicked['lyr'+layer.lyr_id] }" #click="onChecked"></div>
Add below method:
onChecked() {
this.$set(this.isTicked,'lyr'+this.layer.lyr_id, !this.isTicked['lyr'+this.layer.lyr_id])
}
VueJS watches data by reference so to update object in state you need create new one.
onChecked(lyr_id) {
const key = 'lyr'+lyr_id;
this.isTicked = {...this.isTicked, [key]: !this.isTicked[key]};
}
I've got a list of text input-fields created through a v-for with a v-model to an array. I want to add elements to the array, and thus creating another input-field.
So far all works. The problem is that the new input-fields are somehow all assigned the same index (?) or something else is happening to cause them to display the same value.
I've made this jsfiddle to showcase what I mean. If you press the button twice and then try to edit one of the new input-boxes, then all the new input-boxes will get the edited value. I'd want only the edited input-box to show the input value.
I guess there is something I am overlooking here. Is there anyone who can help with this please?
Javascript:
new Vue({
el: '#app',
data: {
items: [{name: "one", id: 0}],
template: {
name: "two",
id: 2,
},
},
methods: {
addRow: function(){
this.items.push(this.template);
this.items[this.items.length - 1].id = Math.random();
}
}
})
HTML:
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div v-for="(item,index) in items" :key="item.id">
<input v-model="item.name">
</div>
<button v-on:click="addRow">
Add row
</button>
<div>Array content: {{items}}</div>
</div>
Usage:
screenshot of what i'm getting
The problem here is that with array.push(declaredObject) you are adding a reference of template so every change will be reflected in all its references.
You must add a new object with the same properties, you can achieve that in many ways, the more common is Object.assign({}, this.template) and the newest one is Destructuring objects {...this.template}. so in your case It should be this.items.push({...this.template})
try
this.items.push({
name: "two",
id: 2,
});
instead of this.items.push(this.template) because template property is reactive and it will affect other properties that use it
check this fiddle
I'm using Vuetify and its v-select component with multiple option enabled to allow selecting multiple options.
These options represent talent(candidate) pools for my CRM software.
What it needs to do is that when some option in v-select is checked, candidates from checked talent pool are fetched from API and saved to some array (let's call it markedCandidates), and when option is deselected, candidates from that pool need to be removed from markedCandidates array.
The problem is that #change or #input events return complete list of selected options. I need it to return just selected/deselected pool and information if it's selected or deselected, to be able to update the markedCandidates array.
My existing code:
<v-select return-object multiple #change="loadCandidatesFromTalentPool" v-model="markedCandidates" :item-text="'name'" :item-value="'name'" :items="talentPoolsSortedByName" dense placeholder="No pool selected" label="Talent Pools" color='#009FFF'>
<template slot="selection" slot-scope="{ item, index }">
<span v-if="index === 0">{{ item.name }}</span>
<span v-if="index === 1" class="grey--text caption othersSpan">(+{{ talentPools.length - 1 }} others)</span>
</template>
</v-select>
Any idea how to solve this?
As I said, loadCandidatesFromTalentPool(change) returns complete array of v-model (markedCandidates)..
EDIT:
I found this solution, it's more of a workaround actually, would be nice if there was dedicated event for this situation:
https://codepen.io/johnjleider/pen/OByoOq?editors=1011
Actually there is only one event related to changing values of v-autocomplete : #change (See https://vuetifyjs.com/en/components/autocompletes#events). The watch approach is useful if you want to monitor only individual changes. However, if you plan to do this with more selectors, it could be better if you create a custom reusable component with a new attached event (in this example, for the last change).
Vue.component('customselector',{
props:[
"value",
"items"
],
data: function() {
return {
content: this.value,
oldVal : this.value
}
},
methods: {
handleInput (e) {
this.$emit('input', this.content)
},
changed (val) {
oldVal=this.oldVal
//detect differences
const diff = [
...val.filter(x => !oldVal.includes(x)),
...oldVal.filter(x => !val.includes(x))
]
this.oldVal = val
var deleted=[]
var inserted=[]
// detect inserted/deleted
for(var i=0;i<diff.length;i++){
if (val.indexOf(diff[i])){
deleted.push(diff[i])
}else{
inserted.push(diff[i])
}
}
this.$emit("change",val)
this.$emit("lastchange",diff,inserted,deleted);
}
},
extends: 'v-autocomplete',
template: '<v-autocomplete #input="handleInput" #change="changed" :items="items" box chips color="blue lighten-2" label="Select" item-text="name" item-value="name" multiple return-object><slot></slot></v-autocomplete>',
})
Then you can use your component as simple as:
<customselector #lastchange="lastChange" >...</customselector>
methods:{
lastChange: function(changed, inserted, deleted){
this.lastChanged = changed
}
}
The changed only shows items which are actually changed. I've added the inserted and deleted arrays to return new items added or removed from the selection.
Source example: https://codepen.io/fraigo/pen/qQRvve/?editors=1011
Replace
:item-text="'name'" :item-value="'name'"
by
item-text="name" item-value="name"
I'm using vue-select in my app and am trying to prevent an event handler from firing when the default values are first loaded into the vue-select input.
The component looks like this:
<v-select
multiple
v-model="product.recommended_accessories"
label="name"
:options="accessoryOptions"
#input="saveProduct"
#search="onAccessorySearch">
<template slot="option" slot-scope="option">
<h4>{{ option.name }}</h4>
<h5>{{ option.sku }}</h5>
</template>
</v-select>
As you can see, I want to save the product when the user changes the values in this multi-select. It works fine, but with one issue.
The value of the select is tied to the product.recommended_accessories data. Elsewhere in the app, a product is loaded from the server, which includes a recommended_accessories attribute. Loading the product then triggers saveProduct to be called since vue-select set the preselected options for the input, which apparently triggers the #input event.
Is there anyway around this? Maybe I have made some sort of design error here. Or maybe there is a hook I should be using to bind the event handler, or to set some sort of flag indicating that the product is in the process of being loaded and saving the product shouldn't occur.
I'm just trying to avoid saving the product immediately after it's loaded for no reason.
For now I am just tracking an accessoryEventCount variable that gets initialized to 0 whenever a product is loaded. Then I make sure accessoryEventCount > 0 before calling saveProduct on an v-select input event.
It works, but I am still wondering if there is a more elegant solution.
UPDATE
Looks like Vue.nextTick is what I was looking for. Before setting the value of product in code, I set a flag this.isSettingProduct = true. Then I set the product, and call Vue.nextTick(() => { this.isSettingProduct = false });.
Now I can avoid saving the product if this.isSettingProduct == true. Using Vue.nextTick ensures that the flag isn't set back to false until after the asynchronous data update completes.
It looks you should bind prop=onChange, though #input still seems working (check v-select github: line# 544).
Below is my solution, before loading your product, bind onChange with function () {}, after loaded, bind it with the function you like.
Vue.component('v-select', VueSelect.VueSelect)
app = new Vue({
el: "#app",
data: {
accessoryOptions: [
{'name':'a1', 'sku':'a2'},
{'name':'b1', 'sku':'b2'},
{'name':'c1', 'sku':'c2'}
],
product: {
recommended_accessories: []
},
saveProduct: function (){}
},
methods: {
onAccessorySearch: function () {
console.log('emit input')
},
loadProduct: function () {
this.product.recommended_accessories = ['a1', 'b1'] // simulate get data from the server
setTimeout( () => {
this.saveProduct = (value) => {
console.log('emit input', this.product.recommended_accessories)
}
}, 400)
}
}
})
#app {
width: 400px;
}
<script src="https://unpkg.com/vue#latest"></script>
<script src="https://unpkg.com/vue-select#latest"></script>
<div id="app">
<button #click="loadProduct()">Click Me!</button>
<v-select
multiple
v-model="product.recommended_accessories"
label="name"
:options="accessoryOptions"
:on-change="saveProduct"
#search="onAccessorySearch">
<template slot="option" slot-scope="option">
<span>{{ option.name }}</span>
<span>{{ option.sku }}</span>
</template>
</v-select>
</div>