How to move rows up & down using v-data-table? - javascript

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

How to put selected list values inside array in vue

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

Vue array changes add or remove using treeview from vuetify

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]

How to collect array of checkboxes in a dynamic array of items vueJs Vuetify

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.

Vue.js footer component acting on other components?

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"...

Vue js set up v-model with dynamic property

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>

Categories

Resources