Sorting table by project - vue.js

I have an object that looks like this
projects: Object
Admin: Object
Project 1: Array[1]
0: Object
name: Admin Project 1
description: Admin Project 1 Description
Project 2: Array[2]
0: Object
name: Admin Project 2 part 1
description: Admin Project 2 part 1 Description
1: Object
name: Admin Project 2 part 2
description: Admin Project 2 part 2 Description
And what I'm trying to do is to display this information on a table.
The issue I'm having is that because of the way I'm creating the table I'm not able to group them by their sub category (EG: Project 2).
So my table would look like this.
Codepen of how I want it to look like
This is how it is looking like at the moment.
Codepen of the wrong way the table looks like
Here is my code
<table class="table table-bordered table-sm" style="width: 100%" v-for="(value, key) in projects">
<tbody>
<tr style="font-size: 90%; background: #a0a0a0; color: #fafafa; font-weight: normal">
<th colspan="8" style="text-indent: 8px;">{{key}}</th>
</tr>
<tr style="font-size: 90%; background: #d0d0d0; color: #606060;" v-for="(v, k) in value">
<th colspan="7" style="text-indent: 32px;">{{ k }}</th>
</tr>
</tbody>
<tbody>
<tr v-for="(p, kk) in value">
<td>
<table>
<tr v-for="vp in p">
<td>{{ vp.name }}</td>
<td>{{ vp.description }}</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>

The tbody elements are structurize themselves below each other (see table)
If you add the contents of the projects inside the same tbody element as additional rows it should work:
<div id="app">
<table class="table table-bordered table-sm" style="width: 100%" v-for="(value, key) in projects">
<thead>
<tr style="font-size: 90%; background: #a0a0a0; color: #fafafa; font-weight: normal">
<th colspan="8" style="text-indent: 8px;">{{key}}</th>
</tr>
</thead>
<tbody v-for="(v, k) in value">
<tr style="font-size: 90%; background: #d0d0d0; color: #606060;">
<th colspan="7" style="text-indent: 32px;">{{ k }}</th>
</tr>
<tr v-for="vp in v">
<td>{{ vp.name }}</td>
<td>{{ vp.description }}</td>
</tr>
</tbody>
</table>
</div>
EDIT: I update the code to use multiple tbody elements and a thead for the object root – should be semantically more correct I think.

This happens because all your for loop have to be imbricated inside parent divs
Here is a much simpler example using only div.
new Vue({
el: "#app",
data: {
projects: {
Admin: {
'Project 1': [
{name: 'Admin Project 1', description: 'Admin Project 1 Description'}
],
'Project 2': [
{name: 'Admin Project 2 part 1', description: 'Admin Project 2 part 1 Description'},
{name: 'Admin Project 2 part 2', description: 'Admin Project 2 part 2 Description'}
]
}
}
},
methods: {
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(value, key) in projects">
{{key}}
<div v-for="(value2, key2) in value">
{{key2}}
<div v-for="(value3) in value2">
{{value3.name}} - {{value3.description}}
</div>
</div>
</div>
</div>
So in your case you have to think to another model of data.

Observations :
You forget to add :key attribute for each iteration. That is a good practice and helps in rendering the DOM.
You added one extra iteration in your code which causes the issue in displaying the table properly.
Working Demo :
new Vue({
el: "#app",
data: {
projects: {
Admin: {
'Project 1': [
{name: 'Admin Project 1', description: 'Admin Project 1 Description'}
],
'Project 2': [
{name: 'Admin Project 2 part 1', description: 'Admin Project 2 part 1 Description'},
{name: 'Admin Project 2 part 2', description: 'Admin Project 2 part 2 Description'}
]
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table class="table table-bordered table-sm" style="width: 100%" v-for="(value, key) in projects" :key="key">
<thead>
<tr style="font-size: 90%; background: #a0a0a0; color: #fafafa; font-weight: normal">
<th colspan="8" style="text-indent: 8px;">{{key}}</th>
</tr>
</thead>
<tbody v-for="(v, i) in Object.keys(value)" :key="i">
<tr style="font-size: 90%; background: #d0d0d0; color: #606060;">
<th colspan="7" style="text-indent: 32px;">{{ v }}</th>
</tr>
<tr v-for="(p, ii) in value[v]" :key="ii">
<td>
<table>
<tr>
<td>{{ p.name }}</td>
<td>{{ p.description }}</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</div>

Related

How do I target a dynamic second table in vue2

I am using v-for in Nuxt vue2 to create a table with a table with in it but cant find any way to target the second table.
This is the HTML I am using and the command lines I am using to target the data.
The first table appears to work but I have tried everything I can think of to target the second table. Hopefully someone can show me how to do it and more importantly explain why there is a problem with the inner table.
<template>
<div>
Working with the DOM Vue2
<table ref="table1">
<tr v-for="index in 5" :key="index">
<th>Company{{ index }}</th>
<th>
<table ref="tableChild">
<tr v-for="index in 2" :key="index">
<th>Inner table Line{{ index }}</th>
</tr>
</table>
</th>
</tr>
</table>
</div>
</template>
<script>
export default {
mounted() {
//-------- targeting the two tables --------------
console.log("table1:", this.$refs.table1);
console.log("table1-all children:", this.$refs["table1"].children);
console.log("table1-all children0:", this.$refs["table1"].children[0]);
console.log("tableChild:", this.$refs.tableChild);
console.log("tableChild-all children:", this.$refs["tableChild"].children);
console.log("tableChild-all children0:", this.$refs["tableChild"].children[0]);
}
}
</script>
<style>
</style>
To access the inner table data by using ref. Here you go :
this.$refs.tableChild[0].children[0]
Live Demo :
new Vue({
el: '#app',
mounted() {
console.log("table1:", this.$refs.tableChild[0].children[0].innerText);
}
})
table, tr, th {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table ref="table1">
<tr v-for="index in 5" :key="index">
<th>Company{{ index }}</th>
<th>
<table ref="tableChild">
<tr v-for="index in 2" :key="index">
<th>Inner table Line{{ index }}</th>
</tr>
</table>
</th>
</tr>
</table>
</div>

How to get the ID from a table inside a b-modal and display on the page in VUEJS

I am facing issue where i want to get the ID from the table when clicking on the button inside a modal and display it on the main page.
Here is the HTML code
<template>
<div class="hello">
<p>Selected id :</p>
<b-button class="mt-2" variant="primary" v-b-modal.modal-xl>
Select codes
</b-button>
<b-modal id="modal-xl" size="lg" centered hide-footer="true">
<table class="table sortable" data-page-list="[10, 25, 50, 100, all]">
<thead>
<tr>
<th>ID</th>
<th>Code</th>
<th>Button</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2222</td>
<td>
<b-button variant="primary">Select</b-button>
</td>
</tr>
<tr>
<td>2</td>
<td>33333</td>
<td>
<b-button variant="primary">Select</b-button>
</td>
</tr>
<tr>
<td>3</td>
<td>4444</td>
<td>
<b-button variant="primary">Select</b-button>
</td>
</tr>
</tbody>
</table>
</b-modal>
</div>
</template>
Here is the Codesandbox for it.
Any help much appreciated.
One way to do that is to save the selected ID as the local state. In addition, I'd recommend using v-for and an array to store the codes list, and then you can pass the id to the event:
<template>
<div class="hello">
<p>Selected id: {{ selected }}</p>
<b-button class="mt-2" variant="primary" v-b-modal.modal-xl>
Select codes
</b-button>
<b-modal id="modal-xl" size="lg" centered hide-footer="true">
<table class="table sortable" data-page-list="[10, 25, 50, 100, all]">
<thead>
<tr>
<th>ID</th>
<th>Code</th>
<th>Button</th>
</tr>
</thead>
<tbody>
<tr v-for="code in codes" :key="code.id">
<td>{{ code.id }}</td>
<td>{{ code.code }}</td>
<td>
<b-button variant="primary" #click="onSelected(code.id)">Select</b-button>
</td>
</tr>
</tbody>
</table>
</b-modal>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
},
data() {
return {
codes: [
{id: 1, code: 2222},
{id: 2, code: 3333},
{id: 3, code: 4444},
],
selected: null
}
},
methods: {
onSelected: function(id) {
this.selected = id;
}
}
};
</script>
You can check out a working example here.

Can I make a table sortable when it's populated by vue v-for?

My client has asked me to make the following table sortable on all columns:
<table class="table table-striped table-condensed">
<thead>
<tr>
<th style="width: 18%">Customer/Supplier</th>
<th style="width: 7%">Title</th>
<th style="width: 10%">First Name</th>
<th style="width: 10%">Last Name</th>
<th style="width: 10%">Department</th>
<th style="width: 10%">Email Address</th>
<th style="width: 15%">Main Phone</th>
<th style="width: 10%">Catetory</th>
<th style="width: 10%">Position</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in SearchResultContactList" v-bind:key="item.ContactID" v-on:click="viewContact(item.ContactID)">
<td>{{item.CustomerName}}{{item.SupplierName}}</td>
<td>{{item.TitleName}}</td>
<td>{{item.FirstName}}</td>
<td>{{item.LastName}}</td>
<td>{{item.DepartmentName}}</td>
<td>{{item.EmailAddress}}</td>
<td>{{getContactMainPhoneNumber(item)}}</td>
<td>{{item.CategoryName}}</td>
<td>{{item.Position}}</td>
</tr>
</tbody>
I've tried this on the Last Name column
<th data-field="LastName" data-sortable="true" style="width: 10%">Last Name</th>
However, it doesn't work.
Any ideas?
Thanks
I was going to suggest what the comment suggested, so I built a scaled back sample component to demonstrate.
new Vue({
el: '#app',
data: {
contacts: [
{
firstName: 'Aaa',
lastName: 'Www'
},
{
firstName: 'Lll',
lastName: 'Mmm'
},
{
firstName: 'Zzz',
lastName: 'Bbb'
},
]
},
methods: {
sortContacts(property) {
this.contacts.sort((a, b) => (a[property] > b[property]) ? 1 : -1);
}
}
})
th {
cursor: pointer;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id='app'>
<div class="sort-table-by-columns">
<div class="row"><div class="col-md-6">
<table class="table table-bordered">
<thead>
<tr>
<th #click="sortContacts('firstName')">FIRST NAME</th>
<th #click="sortContacts('lastName')">LAST NAME</th>
</tr>
</thead>
<tbody>
<tr v-for="(contact, index) in contacts" :key="index">
<td>{{ contact.firstName }}</td>
<td>{{ contact.lastName }}</td>
</tr>
</tbody>
</table>
</div></div>
</div>
</div>

VueJs router-link not working, but link copied from it works in another tab

I am building an webapp with VueJs. I have three pages, apps, registries, identities. In the apps page there is a table with apps with a link1 (localhost:8080/apps/{appid}/regs) to view it's registry in each row. Similarly in registry page, there is a link2
(localhost:8080/apps/{appid}/regs/{regid}/ids) to view it's app.
My problem is, when I click on link1 it takes me to the registries page, but when I click on link2 it takes me to localhost:8080 in stead of localhost:8080/apps/{appid}/regs/{regid}/ids. But when I copy the address from link2 and paste in address bar it takes me to the desired page. What am I doing wrong?
Also I am getting following warning in the console -
[Vue warn] missing param for named route "regs": Expected "appid" to
be defined
[Vue warn] missing param for named route "ids": Expected "appid" to be
defined
router.js
{
path: "apps",
name: "apps",
component: () => import("./components/protected/Apps"),
},
{
path: "apps/:appid/regs",
name: "regs",
props: true,
component: () => import("./components/protected/Regs"),
},
{
path: "apps/:appid/regs/:regid/ids",
name: "ids",
props: true,
component: () => import("./components/protected/Ids"),
},
Apps.vue
<template>
<table class="table table-striped mg-b-0">
<thead>
<tr>
<th class="tx-left w-5">#</th>
<th class="tx-left">Application</th>
<th class="tx-left text-center w-5">Action</th>
</tr>
</thead>
<tbody>
<tr v-bind:key="index" v-for="(app, index) in apps">
<td class="masked">{{index+1}}</td>
<td>{{app.name}}</td>
<td class="text-center">
<router-link class="bg-white" :to="{ name: 'regs', params: { appid: app.id}}">
<i class="fa fa-border fa-eye" title="View Registries"/>
</router-link>
</td>
</tr>
</tbody>
</table>
</template>
Regs.vue
<template>
<template v-if="appid">
<table class="table table-striped mg-b-0">
<thead>
<tr>
<th class="tx-left w-5">#</th>
<th class="tx-left">Registry</th>
<th class="tx-left text-center w-5">Action</th>
</tr>
</thead>
<tbody>
<tr v-bind:key="index" v-for="(reg, index) in regs">
<td class="masked">{{index+1}}</td>
<td>{{reg.name}}</td>
<td class="text-center">
<router-link class="bg-white" :to="{ name: 'ids', params: { appid: appid, regid: reg.id}}">
<i class="fa fa-border fa-eye" title="View Identities"/>
</router-link>
</td>
</tr>
</tbody>
</table>
</template>
</template>
<script>
export default {
name: "Regs",
props: ['appid'],
....
}
Ids.vue
<template>
<template v-if="appid && regid">
<table class="table table-striped mg-b-0">
<thead>
<tr>
<th class="tx-left w-5">#</th>
<th class="tx-left">Identity</th>
</tr>
</thead>
<tbody>
<tr v-bind:key="index" v-for="(id, index) in ids">
<td class="masked">{{index+1}}</td>
<td>{{id.name}}</td>
</tr>
</tbody>
</table>
</template>
</template>
<script>
export default {
name: "Ids",
props: ['appid','regid'],
....
}
</script>
It seems that you are already using the props appid' and 'regid' where the props are not yet fully ready in your component itself. Thats why the vue returned a warning.
You could put a condition first before you execute your vfor to ensure that the props has already a value. like this one
Regs.vue
<template v-if="appid">
<tr v-bind:key="index" v-for="(reg, index) in regs">
<td class="masked">{{index+1}}</td>
<td>{{reg.name}}</td>
<td class="text-center">
<router-link class="bg-white" :to="{ name: 'ids', params: { appid: appid,
regid: reg.id}}">
<i class="fa fa-border fa-eye" title="View Identities"/>
</router-link>
</td>
</tr>
</template>
Ids.vue
<template v-if="appid && regid">
<tr v-bind:key="index" v-for="(id, index) in ids">
<td class="masked">{{index+1}}</td>
<td>{{id.name}}</td>
</tr>
</template>

Delete row from Vue Js dynamic array

I am trying to create a dynamic table in Vue. I am able to add row properly, but when I try to remove the row, every time it removes the last row by default.
Even if I hard coded the index number, still same result.
Its been 2 days and I am stuck in this problem. Appreciate any help.
My Template:
<div class="box-body">
<table id="myTable" class="table">
<thead>
<tr class="timesheetTableHeader">
<th style="width: 50rem">Project</th>
<th style="width: 40rem">Activity</th>
<th style="width: 20rem">Charge Back</th>
<th style="width: 10rem">Hours</th>
</tr>
</thead>
<div class="mt-2"></div>
<tbody>
<tr v-for="(row, index) in rows">
<td>
<mwselect :options="Project"
v-model="row.projectName"
></mwselect>
</td>
<td>
<mwselect :options="Activity"
v-model="row.activity"
></mwselect>
</td>
<td>
<mwselect :options="CostCenter"
v-model="row.chargeBackCC"
></mwselect>
</td>
<td>
<b-form-input type="number"
class="pb-1 pt-1"
required
v-model="row.hours"
name="Hours">
</b-form-input>
</td>
<td>
<a #click="removeElement(index)" style="cursor: pointer">
<i class="fa fa-trash-o pt-1" style="color:red; text-shadow: 1px 1px 1px #ccc;
font-size: 1.5em;" title="Delete Line"
></i></a>
</td>
</tr>
</tbody>
</table>
</div>
My Script:
<script>
var period_name = ''
var start_date = ''
var end_date = ''
var counter = 0
export default {
components: {
mwselect
},
data() {
return {
Project: projects,
CostCenter: costcenter,
Activity: activities,
rows: []
} // return end
},
methods: {
createLine(index) {
var elem = document.createElement('tr');
this.rows.push({
projectName: "",
activity: "",
chargeBackCC: "",
hours: ""
})
},
removeElement: function (index) {
alert(index)
this.rows.splice(index, 1);
}
}
}
</script>
Ordering is not guaranteed unless you put key attribute to the for loop. So this change to your tag must solve the problem:
<tr v-for="(row, index) in rows" :key="index">
Here is more information about the attribute key: https://v2.vuejs.org/v2/guide/list.html#key
Thanks Cmertayak,
Adding :key="index" worked and it solved my problem.