I wanted to know if it is possible to move rows up & down?
I was using a checkbox feature, & the CRUD data table from the documetation.
I couldn't really find any examples in the documentation.
My v-data-table currently looks like this
<v-data-table
v-model="selected"
:headers="headers"
:items="rows"
:search="search"
disable-pagination
hide-default-footer
show-select
class="elevation-1" >
<template v-slot:item="props">
<tr>
<td>
<v-checkbox
v-model="props.selected"
:disabled="!props.selected && selected.length != 0"
:indeterminate="!props.selected && selected.length != 0"
></v-checkbox>
</td>
<td v-for="(prop, key) in props.item" :key="key" #click="onClickItem(key, props.item[key])">
{{props.item[key]}}</td>
<td>
<v-icon small class="mr-2" #click="editItem(props.item)">
mdi-pencil
</v-icon>
<v-icon small #click="deleteItem(props.item, getItemAtIndex(navItem))">
mdi-delete
</v-icon>
</td>
</tr>
</template>
<template> <!-- A dialog box for editing content-->
</template>
</v-data-table>
You can take a look at this example. The example has up and down arrow which you will click and it will update the items array. Note that you must you use Vue.$set to make the update to the items array reactive.
The example is done using vue-composition api and typescript
https://gist.github.com/JeremyWalters/457ea585bab678b3bafeb3ee16e96401
<template>
<v-data-table :headers="headers" :items="items">
<template v-slot:item.actionUp="{item}">
<v-btn color="success" icon #click="moveUp(item.id)">
<v-icon>mdi-arrow-up</v-icon>
</v-btn>
</template>
<template v-slot:item.actionDown="{item}">
<v-btn color="warning" icon #click="moveDown(item.id)">
<v-icon>mdi-arrow-down</v-icon>
</v-btn>
</template>
</v-data-table>
</template>
<script lang="ts">
import {
defineComponent,
SetupContext,
ref,
onMounted,
Ref
} from "#vue/composition-api";
export default defineComponent({
setup(props: any, context: SetupContext) {
const items: Ref<any[]> = ref([]);
const headers = [
{ text: "Test Value", value: "testValue" },
{ text: "", value: "actionUp" },
{ text: "", value: "actionDown" }
];
// Create data example
onMounted(() => {
for (let i = 0; i < 20; i++) {
items.value.push({ id: i, testValue: "testValue " + i });
}
});
// Move items up in the array
function moveUp(id: number) {
const index = items.value.findIndex(e => e.id == id);
if (index > 0) {
const el = items.value[index];
context.root.$set(items.value, index, items.value[index - 1]);
context.root.$set(items.value, index - 1, el);
}
}
// Move items down in the array
function moveDown(id: number) {
const index = items.value.findIndex(e => e.id == id);
debugger;
if (index !== -1 && index < items.value.length - 1) {
const el = items.value[index];
context.root.$set(items.value, index, items.value[index + 1]);
context.root.$set(items.value, index + 1, el);
}
}
return {
moveUp,
moveDown,
headers,
items
};
}
});
</script>
Related
I have a vue application where I have to select two elements from an list component and then put them inside an array.Right now I have my list and I can select them thanks to vuetify I binded it to an array with v-model I can console log it inside of an array but what I want to achieve is something like this:
{
"meetingName":"",
"meetingUrl":"",
"participants":{
participant1: "Hasan",
participant2: "Turan"
}
}
instead I am getting right now this:
{
"meetingName":"",
"meetingUrl":"",
"participants":[
"Hasan",
"Turan"
]
}
Could someone look at my code and tell me what is wrong with it?
html:
<template>
<v-container>
<v-row>
<v-col cols="4">
<v-card>
<v-list>
<v-list-item-group
v-model="model"
multiple
color="indigo"
v-model="model.participants"
>
<v-list-item
v-for="(item, i) in voterArrayFilteredByTime"
:key="item.voterUniqueName"
:value="item.voterUniqueName"
v-on:click= "generateGroup"
>
<v-list-item-content>
<v-list-item-title v-text="item.voterUniqueName"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-card>
</v-col>
</v-container>
</template>
an here is the method with which I want to console log it but it does not work like I said I am just getting numbers.
<script>
import axios from "axios";
export default {
name: "AddGroupsModal",
data : ()=>({
singleSelect: false,
selection: "",
model:{ meetingName: "", meetingUrl: "", participants: [] },
methods: {
generateGroup(){
console.log(this.model)
}
}
</script>
One of the problems in your markup is it has two bindings to v-model when there should be just one (the last one in this case):
<v-list-item-group
v-model="model"❌
multiple
color="indigo"
v-model="model.participants"❌
>
The v-list components can't create the expected value format, but you can use a computed prop along with Array.prototype.reduce to create an object from the array entries:
export default {
computed: {
computedParticipants() {
return this.model.participants.reduce((obj, name, i) => {
obj[`participant${i + 1}`] = name
return obj
}, {})
// OR:
const obj = {}
this.model.participants.forEach((name, i) => {
obj[`participant${i + 1}`] = name
})
return obj
},
},
}
demo
I use vuetify and treeview. I want to list object fill with items and nested array in every of them.
export interface IExamMenuItem {
id?: number | string;
title: string;
link: string;
children: IExamMenuItem[];
}
<v-treeview :items="items" item-text="title" item-key="items.id" dense hoverable>
<template #prepend="{ item, open }">
<v-icon v-if="item.title === 'Амбулаторен лист'">
{{ 'home' }}
</v-icon>
<v-icon v-else-if="!item.name != 'Амбулаторен лист'">
{{ open ? 'mdi-folder-open' : 'mdi-folder' }}
</v-icon>
</template>
<template #label="{ item }">
<v-list-item
v-if="item.children"
:key="item.id"
style="background-color: beige"
:to="item.link"
link
>
{{ item.title }}
</v-list-item>
<v-list-item v-else :key="item.id" :to="item.link" link>
{{ item.title }}
</v-list-item>
</template>
</v-treeview>
When im trying to add or remove from 'items' i doesnt effect the tree, 'items' are update but not in the tree.
Here is the add method
private createReferralEventHandler(id: number | null, typeOfReferral: string | null) {
if (id != null && typeOfReferral != null) {
let item: IExamMenuItem = {} as IExamMenuItem;
const main = this.items.filter(x => x.id === typeOfReferral)[0];
if (main) {
item = {
id: id,
title: `${main.title} №${id}`,
link: `/Exam/${typeOfReferral}/Edit/${id}`
} as IExamMenuItem;
if (!main.children) {
main.children = [];
}
this.$set(this.items.filter(x => x.id === typeOfReferral)[0]['children'], main.children.length, item)
main.children.push(item);
this.titleOfReferral = item.title;
this.alertCreate = true;
}
}
Here is the the delete method
private deleteReferralEventHandler(id: number | null, typeOfReferral: string | null) {
if (id != null) {
const main = this.items.filter((x) => x.id === typeOfReferral)[0];
if (main) {
let remove = main.children.findIndex((i) => i.id === id);
this.titleOfReferral = main.children.filter((i) => i.id === id)[0].title;
main.children.splice(remove, 1);
this.alertDelete = true;
}
}
}
You have to re-assign items value to force vue to re-render the tree.
Should use something like this to remove an item:
this.items = this.items.filter((x) => x.id !== idToRemove)
And something like this to add an item:
this.items = [...this.items, newItem]
I have a role permission implementation where by i have resource which is an array that contains items that the user may be given access to. again i have checks which are the permissions for the said resource.
I Want to dynamically assign a role that will have these resources along with the permissions chosen by the user.
i have tried the following implementation but the submitted result does not submit an array of permissions (checks) chosen, it only submits the last item in the checkbox.
What am i missing here
The template is
<template v-slot:activator="{ on }">
<v-btn
class="mx-2"
data-toggle="tooltip"
data-placement="left"
title="Edit Permissions"
fab
dark
small
color="#666"
v-on="on"
#click="getItem()"
>
<v-icon dark>mdi-access-point</v-icon>
</v-btn>
</template>
<v-form v-model="valid" #submit.prevent="onSubmit">
<v-container>
<v-row>
<v-col cols="12" sm="12" md="12" class="alternate-card">
<h4 class="text-muted text-center">
Role : {{ payload.role }}
</h4>
</v-col>
<blockquote class="col-md-12">
<h4 class=" text-center">Permissions</h4>
</blockquote>
<hr />
<v-col
cols="12"
sm="12"
md="12"
v-for="(perm, indexe) in result"
:key="indexe"
>
<h5 class="text-center text-muted">{{ indexe }}</h5>
<v-row class="alternate-card">
<v-col
cols="12"
sm="3"
md="3"
v-for="(item, index) in checks"
:key="index"
>
{{item}}
<span>
<v-checkbox
v-model="result[indexe]"
:label="item"
:value="item"
></v-checkbox>
</span>
</v-col>
</v-row>
</v-col>
</v-row>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="dialog = false"
>Close</v-btn
>
<v-btn type="submit" :disabled="!valid" color="blue darken-1" text
>Save</v-btn
>
</v-card-actions>
</v-container>
</v-form>
The script is
export default {
props: ["form"],
data() {
return {
valid: false,
load: false,
payload: {},
result: {},
checks: {
create: "create",
edit: "edit",
delete: "delete",
show: "show",
},
};
},
methods: {
...mapActions(["editRole"]),
onSubmit() {
this.load = true;
console.log(this.result)
this.payload.permission = {}
this.payload.permission = this.result
// console.log(this.payload)
if (this.editRole(this.payload)) {
setTimeout(() => (this.load = false), 1000);
setTimeout(() => (this.dialog = false), 1300);
}
},
getItem() {
this.payload = {
id: 22462662,
role: "Admin",
permission :
{
package : ['create', 'edit', 'delete', 'show'],
category : ['create', 'edit', 'delete', 'show'],
product : ['create', 'edit', 'delete', 'show'],
},
},
let resource = ['package', 'users', 'category', 'product', 'assets', 'readers']
let keys = Object.keys(this.payload.permission);
for (var i = 0; i < resource.length; i++) {
for (var j = 0; j < Object.keys(this.payload.permission).length; j++) {
//search through the obj and return corresponding items and assign to result Obj
if (keys[j] === resource[i]) {
this.result[resource[i]] = this.payload.permission[keys[j]];
}
}
}
//now remove all keys that are in obj from resource
let included = resource.filter(function(item) {
return !keys.includes(item);
});
//now push these into result
for (var h = 0; h < included.length; h++) {
this.result[included[h]] = [];
}
},
},
};
so at the end of the day result Obj will look something like
{ "package": [ "create", "edit", "delete", "show" ], "category": [ "create", "edit", "delete", "show" ], "product": [ "create", "edit", "delete", "show" ], "assets": [], "users": [], "readers": [] }
I am using Vuex, but for this problem i have removed most references to it.
Thanks #IVOGELOV i figured it out, though it is not as neat, but will have to do for now, i changed the
<v-checkbox v-model="result[indexe][index]" :label="item" :value="item" ></v-checkbox>
and
`checks: { 0: "create", 1: "edit", 2: "delete", "show",},`
and that did it, i got the array of checkboxes, i don't know why it worked, but it did. we'll improve my structure going forward but this will do for now.
I'm new to Vue.js and building an app with header / footer (showing on certain pages) and in the footer I have a shopping cart.
Basically on some pages of the app I can add items to the cart, and with the icon in the footer I can acess to the list of items, and delete them if I want to.
My cart showing as a dialog full screen, above my app.
The problem is, if I empty my cart, I want to reload the component behind my shopping cart before closing it (because the step is not the same...), and I don't know how to do it, actually i'm reloading the page if I close the cart while he's empty....
This is my footer part (with the cart) :
var footer = Vue.component("iteck-footer", {
template: `<div>
<v-footer fixed height="auto" color="teal" dark>
<v-layout class="pl-2 pr-2" justify-center row wrap>
<v-dialog fullscreen v-model="dialog" transition="dialog-bottom-transition">
<v-toolbar fixed dense dark color="teal">
<v-spacer></v-spacer>
<v-toolbar-title>Panier</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items>
<v-btn icon dark #click="dialog = false">
<v-icon>close</v-icon>
</v-btn>
</v-toolbar-items>
</v-toolbar>
<div class="content-with-header-footer">
<div v-if="articles.length > 0">
<div v-for="article in articles" :key="article.LIG">
<v-checkbox class="inputs-panier pa-0 ma-0 " v-model="article.selected">
<template slot="label">
<div>{{article.DESIG}} | <b>Qté.</b> : {{quantite(article.QTESAI)}}</div>
<div>{{article.CLE}} {{article.CODBAR}}</div>
</template>
</v-checkbox>
</div>
</div>
<div class="text-xs-center mt-4" v-else>
<h2 class="red--text">Panier vide</h2>
</div>
</div>
<v-footer fixed height="auto" color="teal" dark>
<v-btn #click="deleteLignesPanier()" class="mx-0" icon>
<v-icon>delete</v-icon>
</v-btn>
<v-spacer></v-spacer>
<v-btn #click="validerPanier()" class="mx-0" icon>
<i class="material-icons">check</i>
</v-btn>
</v-footer>
</v-dialog>
<v-btn style="position:relative;" #click="openMenu()" class="mx-0" icon>
<i class="material-icons">shopping_basket</i>
<span id="nb_articles">
{{articles.length}}
</span>
</v-btn>
<v-spacer></v-spacer>
<v-btn #click="validerPanier()" class="mx-0" icon>
<i class="material-icons">check</i>
</v-btn>
<v-dialog v-model="modal" max-width="290">
<v-card>
<v-card-text>
{{modalMessage}}
</v-card-text>
<v-card-actions>
<v-btn color="green darken-1" flat="flat" #click="modal = false; modalMessage = ''">
OK
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout>
</v-footer>
</div>`,
props: ["menu"],
data() {
return {
modal : false,
modalMessage : "",
entete: null,
articles: [],
dialog: false
}
},
mounted() {
this.getPanier()
},
watch: {
// whenever question changes, this function will run
dialog: function (newValue, oldValue) {
if(newValue === false && this.articles.length === 0)
location.reload();
}
},
methods: {
async openMenu() {
this.articles = [];
this.entete = null;
if(this.dialog === false) {
//Récuperation du panier en cours.
this.getPanier();
}
this.dialog = true;
},
async deleteLignesPanier() {
let lignes = "";
for (let i=0; i < this.articles.length; i++ ) {
if(this.articles[i].selected === true) {
if(lignes === "")
lignes = lignes + this.articles[i].LIGNE;
else
lignes = lignes + "," + this.articles[i].LIGNE;
}
}
if(lignes !== "") {
//On envoie la requête au serveur.
let data = await wspda.deleteLignesPanier(this.menu,lignes);
if(data == 'true')
{
console.log('OUI');
console.log('length',this.articles.length);
console.log('articles',this.articles);
for (let i = this.articles.length -1; i >= 0; i-- ) {
if(this.articles[i].selected === true) {
this.articles.splice(i,1);
}
}
}
else
{
console.log(data);
}
}
},
async validerPanier() {
if(this.articles.length > 0) {
let data = await wspda.validerPanier(this.menu,"","False");
this.modal = true;
this.modalMessage = data;
this.articles = [];
}
},
async getPanier() {
let data = await wspda.getPanier(this.menu);
if(data.ProDataSet && data.ProDataSet.PANIERENT && data.ProDataSet.PANIERLIG) {
if(Array.isArray(data.ProDataSet.PANIERLIG)) {
this.articles = data.ProDataSet.PANIERLIG;
} else {
this.articles.push(data.ProDataSet.PANIERLIG);
}
this.entete = data.ProDataSet.PANIERENT;
for (let i=0; i<this.articles; i++ ) {
this.articles[i].selected = false;
}
}
},
quantite(qtesai) {
return (qtesai % 1) === 0 ? Math.round(qtesai) : qtesai;
}
}
});
As you can see, in my watcher, on the closing of the cart, i may reload the page... But there is a way, when the cart is empty (deleting items), reload the current component behind my dialog ?? can't find a proper way to do it.
And my current component running behind (just for an example) :
var spaHome = Vue.component("Home", {
template: `<div>
<v-container fill-height>
<v-layout>
<v-flex>
<v-list>
<v-list-tile class="menu-home"
v-for="(item,index) in menus" :key="index" #click="$router.push(item.path)" v-if="item.value">
<!--<v-list-tile-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-tile-action>-->
<v-list-tile-content class="teal darken-3 white--text">
<v-list-tile-title class="text-xs-center">{{ item.label }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-flex>
</v-layout>
</v-container>
</div>`,
props: ["title"],
$_veeValidate: {
validator: "new"
},
data() {
return {
menus: conf.Menus,
}
},
methods: {
}
});
By the way, I'm not using node.js etc... Because I can't run the app on a node server, so I can't use "import" , "export"...
How can I setup v-model using dynamic keys with v-for? The code below does not update the model.
I am using vuetify framework.
My code is below
Template
<v-card v-for="(value, key) in myData">
<v-checkbox v-model='selected[key]' :name="key" ></v-checkbox>
</v-card>
Script
export default {
props: {
myData : Object
},
data: function(){
return {
selected: {},
active: null,
}
},
methods: {
setModels: function()
{
let data = this.myData;
let sel = this.selected;
Object.keys(data).forEach(function(key){
if(typeof data[key]== 'object')
{
sel[key] = []
}
});
},
},
mounted() {
this.setModels();
}
}
You could create a method:
methods: {
isSelected (key) {
return this.selected.includes(key);
}
}
and set v-model to v-model="isSelected(key)"
try:
<v-card v-for="(item, index) in myData">
<v-checkbox v-model="item" :name="index" ></v-checkbox>
</v-card>
What i dont get is.. Why is your myData a obj and not an array of obj? Then you could do this:
<v-card v-for="item in myData">
<v-checkbox v-model="item.checked" :name="item.name" ></v-checkbox>
</v-card>