I'm using Vue.Draggable plugin to implement a draggable list. I'm passing a sorted computed property, like the following:
data(){
return {
paymentMethods: [
{ name: 'stripe', Dindex: 0, state: 2 },
{ name: 'paypal', Dindex: 1 , state: 1 },
{ name: '2checkout', Dindex: 2, state: 4 },
{ name: 'cod', Dindex: 3, state: 3 }
],
}
},
computed: {
payments() {
return _.sortBy(this.paymentMethods, 'state');
},
}
Draggable list:
<draggable :list="payments" class="payment-methods" tag="ul" #start="drag=true" #end="drag=false" #change="indexChanged">
<li v-for="(method, index) in payments" :key="index">
<!-- list data -->
</li>
</draggable>
The problem here is the draggable list never works because i'm forcing the list sorting (using lodash _.sortBy), the question is how i can sort inside a draggable list.
While it is a computed value the list will sorted again when you drag it. I think the best thing to do is sort it when mounted:(So only initially)
data() {
return {
payments: []
paymentMethods: [
{ name: 'stripe', Dindex: 0, state: 2 },
{ name: 'paypal', Dindex: 1 , state: 1 },
{ name: '2checkout', Dindex: 2, state: 4 },
{ name: 'cod', Dindex: 3, state: 3 }
],
}
}
mounted() {
payments = _.sortBy(this.paymentMethods, 'state');
}
I know this is kind of old but I've also tried Googling for a solution and couldn't find any. A few minutes later I came up with this:
// <template />
<draggable
class="drag-wrapper selected-cols"
v-bind="dragOptions"
...other props
#change="colChange"
>
<div v-for="(col, i) in availableColumns" :key="col.name + '-' + i">
<div class="drag-area">
{{ col.label }}
</div>
</div>
</draggable>
// <scripts />
methods: {
colChange(e) {
this.availableColumns.sort((a, b) =>
a.label.localeCompare(b.label));
},
},
So instead of using a computed for sorting, I just do the sorting in the #change listener since that is being called AFTER vuedraggable is done sorting.
Related
I created small piece of code to search from given array in search bar. As i unserstood it is better to use computed function over method but when I put function in it dosen't work and give me an error: Duplicate keys detected: '3'. This may cause an update. So how can I solve it?
My function:
data() {
return {
RMnames: [],
filteredNames: [
{ name: "Zales", category: "Jewelry", id: 1 },
{ name: "Lux", category: "Jewelry", id: 2 },
{ name: "Edited", category: "Jewelry", id: 3 },
{ name: "Editedsdsdxs", category: "Jewelry", id: 3 },
],
searchTerm: "",
Names: [],
};
},
computed: {
filteredFunction() {
this.Names = this.filteredNames.filter((n) => {
return n.name.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1;
});
},
},
My template:
<v-col align-self="start">
<v-text-field
v-model="searchTerm"
placeholder="Search"
#input="filteredFunction"
></v-text-field>
<v-card v-for="item in filteredNames" :key="item.id">
<v-card-title>{{ item.name }}</v-card-title>
</v-card>
I have a checkbox with the following events and props:
<v-checkbox
v-for="planets in allPlanets" :key="`planets_${planets.id}`"
:label="planets.name"
:value="planets.id"
v-model="selectedPlanets"
/>
Given that all of the checkboxes are brought in using a v-for, how can I check whether a checkbox is selected using a method or mounted function in Vue JS?
For example:
methods: {
checkSelected() {
????
},
Add a planets.selected key.
allPlanets: [
{ name: 'Planet name', value: 'Planet value' , selected : false },
...,
...
],
}
And in your template:
<v-checkbox
v-for="planets in allPlanets" :key="`planets_${planets.id}`"
:label="planets.name"
:value="planets.id"
v-model="planets.selected"
/>
Similar to:
Display multiple checkbox in table format using <v-for> and <v-checkbox> in Vuetify?
you have to make such a structure, so you know for each id, whether it is checked or not
new Vue({
data: () => ({
allPlanets: [
{
id: 32,
name: "planent",
selected: false
},
{
id: 365,
name: "planet 2",
selected: false
}
],
}),
methods: {
checkSelectedByIndex(index) {
return this.allPlanets[index].selected
},
checkSelectedById(id) {
return this.allPlanets.find(p => p.id === id)?.selected ?? false
}
}
});
and in the you have to set the v-model="planets.selected"
Given your layout the simplest methods is:
methods: {
checkSelected(id) {
return this.selectedPlanets.includes(id)
},
}
I have a Vue component receiving an array of 'items' from its parent.
I've sorted them into categories, two 'items' in each category:
computed: {
// sort items into categories
glass: function() {
return this.items.filter(i => i.category === "glass").slice(0, 2);
},
ceramics:
// etc...
I need to place both items in categories.items to then pass them as props to another component:
data() {
return {
categories: [
{ name: "Glass", sort: "glass", items: {} },
{ name: "Ceramics", sort: "ceramics", items: {} },
{ name: "Brass", sort: "brass", items: {} },
{ name: "Books/Comics", sort: "books", items: {} },
{ name: "Collectibles", sort: "collectibles", items: {} },
{ name: "Pictures", sort: "pictures", items: {} },
{ name: "Other", sort: "other", items: {} }
]
};
},
When I use created or mounted nothing is passed through, when I use beforeDestroy or destroy and console.log the results it works fine, but, they're of no use when exiting the page.
The 'items' are from an Axios GET request, could this be why?
GET request from parent component:
methods: {
fetchItems() {
// items request
let uri = "http://localhost:8000/api/items";
this.axios.get(uri).then(response => {
// randomize response
for (let i = response.data.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[response.data[i], response.data[j]] = [
response.data[j],
response.data[i]
];
}
this.items = response.data;
});
}
},
Passing props to child component:
<div
class="items-container"
v-for="category in categories"
:key="category.name"
>
<router-link :to="'category.link'" class="category-names-home-link">
<h2 class="category-names-home">{{ category.name }}</h2>
</router-link>
<router-link
:to="'category.link'"
class="home-view-all"
#mouseover.native="expand"
#mouseout.native="revert"
>View All...</router-link
>
<div class="items">
<!-- Pass to child component as props: -->
<SubItem :item="categories.items" />
<SubItem :item="categories.items" />
</div>
</div>
Don't bother adding the items to the categories, keep them separate
Instead of multiple computeds, use one computed object hash to store all the filtered sets:
computed: {
filtered() {
if (!this.items) return null;
const filtered = {};
this.items.forEach(item => {
if (filtered[item.category]) {
filtered[item.category].push(item);
} else {
filtered[item.category] = [item];
}
});
return filtered;
}
}
Result:
{
'Glass': [ ... ],
'Ceramic': [ ... ]
...
}
In the template:
<div>
<div v-for="category in categories" :key="category.name">
<div class="items" v-for="item in filtered[category.name]">
<SubItem :item="item" />
</div>
</div>
</div>
You can use a v-if in the parent to prevent displaying anything until the data is loaded:
<display v-if="items" :items="items"></display>
Here is a demo
I am attempting to setup a Vuetify treeview component to enable the pushing of input into the nested array of existing items. So far, I have been able to set up the component to enable the pushing of input into the nested array of a dynamically added item. However, I want to push new input into the nested array of the existing 'applications' item, instead of creating an entirely new item. How can I achieve this? See my code below. Thanks!
<template>
<v-app data-app>
<v-app-bar app flat>
<v-text-field v-model="newItem" placeholder="add new item"></v-text-field>
<div class="flex-grow-1"></div>
<v-btn color="success" #click="addItem()">
Add Item
</v-btn>
</v-app-bar>
<v-treeview :items="items"></v-treeview>
</v-app>
</template>
<script>
export default {
data: () => ({
newItem: null,
items: [
{
id: 1,
name: 'Applications :',
children: [
{ id: 2, name: 'Calendar : app' },
{ id: 3, name: 'Chrome : app' },
{ id: 4, name: 'Webstorm : app' },
],
},
],
}),
methods: {
addItem () {
this.items.push({
name: 'Applications :',
children: [
{ id: 5, name: this.newItem },
],
})
}
}
}
</script>
I'm using Vue.js 2.0 and the Element UI library.
I want to use a multiple select to attribute some roles to my users.
The list of all roles available is received and assigned to availableRoles. Since it is an array of object and the v-model accepts only an array with value, I need to extract the id of the roles trough the computed property computedRoles.
The current roles of my user are received and assigned to userRoles: [{'id':1, 'name':'Admin'}, {'id':3, 'name':'User'}].
computedRoles is then equals to [1,3]
The preselection of the select is fine but I can't change anything (add or remove option from the select)
What is wrong and how to fix it?
http://jsfiddle.net/3ra1jscx/3/
<div id="app">
<template>
<el-select v-model="computedRoles" multiple placeholder="Select">
<el-option v-for="item in availableRoles" :label="item.name" :value="item.id">
</el-option>
</el-select>
</template>
</div>
var Main = {
data() {
return {
availableRoles: [{
id: 1,
name: 'Admin'
}, {
id: 2,
name: 'Power User'
}, {
id: 3,
name: 'User'
}],
userRoles: [{'id':1, 'name':'Admin'}, {'id':3, 'name':'User'}]
}
},
computed : {
computedRoles () {
return this.userRoles.map(role => role.id)
}
}
}
I agree mostly with #wostex answer, but he doesn't give you the userRoles property back. Essentially you should swap computedRoles and userRoles. userRoles becomes a computed property and computedRoles is a data property. In my update, I changed the name of computedRoles to selectedRoles.
var Main = {
data() {
return {
availableRoles: [{
id: 1,
name: 'Admin'
}, {
id: 2,
name: 'Power User'
}, {
id: 3,
name: 'User'
}],
selectedRoles:[1,2]
}
},
computed : {
userRoles(){
return this.availableRoles.reduce((selected, role) => {
if (this.selectedRoles.includes(role.id))
selected.push(role);
return selected;
}, [])
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
And here is the fiddle.
Check the solution: jsfiddle
The caveat here is that computed properties are getters mainly. You can define setter for computed property, but my approach is more vue-like in my opinion.
In short, instead of v-model on computed set v-model for data property.
Full code:
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui/lib/index.js"></script>
<div id="app">
<template>
<el-select v-model="ids" multiple placeholder="Select" #change="logit()">
<el-option v-for="item in availableRoles" :label="item.name" :value="item.id">
</el-option>
</el-select>
</template>
</div>
var Main = {
data() {
return {
availableRoles: [{
id: 1,
name: 'Admin'
}, {
id: 2,
name: 'Power User'
}, {
id: 3,
name: 'User'
}],
userRoles: [{'id':1, 'name':'Admin'}, {'id':3, 'name':'User'}],
ids: []
}
},
mounted() {
this.ids = this.userRoles.map(role => role.id);
},
methods: {
logit: function() {
console.log(this.ids);
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')