Listing grouped data in table rows - vue.js

I assume I have the following data
data() {
return {
users: [
{country: 'USA', name: 'Taylor'},
{country: 'UK', name: 'Tony'},
{country: 'USA', name: 'Mary'},
{country: 'JAPAN', name: 'Jane'},
{country: 'JAPAN', name: 'Moses'},
// More users from different countries in the world
]
}
}
I want to group by country and my final table structure to be like this, ordered by country name desc.
<table>
<tr>
<th>Country</th>
<th>User</th>
</tr>
<tr>
<td colspan="2">USA</td>
</tr>
<tr>
<td></td>
<td>Taylor</td>
</tr>
<tr>
<td></td>
<td>Mary</td>
</tr>
<tr>
<td colspan="2">UK</td>
</tr>
<tr>
<td></td>
<td>Tony</td>
</tr>
<tr>
<td colspan="2">JAPAN</td>
</tr>
<tr>
<td></td>
<td>Jane</td>
</tr>
<tr>
<td></td>
<td>Moses</td>
</tr>
</table>
How can I achieve this? I have tried playing with Lodash's groupBy but cant achieve it
let users = _.groupBy(this.users, function(user) { return user.country })

Here is one example of how to do it without any libraries.
console.clear()
new Vue({
el: "#app",
data:{
users: [
{country: 'USA', name: 'Taylor'},
{country: 'UK', name: 'Tony'},
{country: 'USA', name: 'Mary'},
{country: 'JAPAN', name: 'Jane'},
{country: 'JAPAN', name: 'Moses'},
// More users from different countries in the world
]
},
computed:{
byCountry(){
return this.users.reduce((acc, user) => {
(acc[user.country] = acc[user.country] || []).push(user.name)
return acc
}, {})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.9/vue.js"></script>
<div id="app">
<table>
<tr>
<th>Country</th>
<th>User</th>
</tr>
<template v-for="people, country in byCountry">
<tr>
<td colspan="2">{{country}}</td>
</tr>
<tr v-for="person in people">
<td></td>
<td>{{person}}</td>
</tr>
</template>
</table>
</div>

Related

In vuejs, quoting variables in HTML element attribute doesn't work

Here is the code:
<tr v-for="(item, index) in detail" :key="item.name" class="[[ item.name ]]">
<td>[[ index + 1 ]]</td>
<td>[[ item.name ]]</td>
The rendered HTML looks like this:
<tr class="[[ item.name ]]">
<td>1</td>
<td>Job</td>
</tr>
<tr class="[[ item.name ]]">
<td>2</td>
<td>Jesse</td>
</tr>
<tr class="[[ item.name ]]">
<td>3</td>
<td>Wazert</td>
</tr>
The class="[[ item.name ]]" just don't change. What I expect is:
<tr class="Job">
<td>1</td>
<td>Job</td>
</tr>
<tr class="Jesse">
<td>2</td>
<td>Jesse</td>
</tr>
<tr class="Wazert">
<td>3</td>
<td>Wazert</td>
</tr>
How should I fix it?
First thing Square bracket not worked in vue.js you need to use interpolation for binding the data dynamically.
So you need to use like For Example
HTML
<table border="1">
<tr v-for="(item, index) in detail" :key="item.name" :class="item.name">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
</tr>
</table>
JS
data: function () {
return {
detail: [{ name: "Job" }, { name: "Jesse" }, { name: "Wazert" }],
};
},
Here you can play with code
You need to use class-binding and interpolate the data:
new Vue({
el: "#app",
data: () => ({
detail: [ { name: "Job" }, { name: "Jesse" }, { name: "Wazert" } ]
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tr v-for="(item, index) in detail" :key="item.name" :class="item.name">
<td>{{index + 1}}</td>
<td>{{item.name}}</td>
</tr>
</table>
</div>

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>

how to build dynamic table with vue?

I tried to find the way build table from the following data property:
data(){
return {
headlist: [
{row: ID},
{row: Name},
{row: Title},
{row: Description },
{row: Actions }
],
}
Template code:
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Title</th>
<th>Description</th>
<th>Actions</th>
</tr>
Now tried to replace to :
<thead>
<tr v-repeat="headlist ">
<th>{{row}}</th>
</tr>
</thead>
Found example from https://012.vuejs.org/guide/list.html
What is wrong my code?
That's the documentation for the older version of VueJS. You shouldn't be referring to that. Also, you should be using the v-for directive:
<th v-for="(entry, i) in headlist" v-bind:key="i">
{{ entry.row }}
</th>
Proof-of-concept:
new Vue({
el: '#app',
data: function() {
return {
headlist: [{
row: 'ID'
},
{
row: 'Name'
},
{
row: 'Title'
},
{
row: 'Description'
},
{
row: 'Actions'
}
],
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table class="table table-bordered">
<thead>
<tr>
<th v-for="(entry, i) in headlist" v-bind:key="i">
{{ entry.row }}
</th>
</tr>
</thead>
</table>
</div>

Data is not showing up in html

I am running into a issue with showing the data from my variable, itemListPrice. I have checked in the console and the data is populated into the itemListPrice, but its not showing up in my html, am I loading it all wrong?
Here is the markup
<div id="app2">
<div id="TableContainer" style="width:798px !important">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<td><label>Catalog Name</label></td>
</tr>
</thead>
<tbody>
<tr>
<td>{{ currentCatalogName }}</td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td><label>Item Name</label></td>
</tr>
</thead>
<tbody>
<tr>
<td> {{ itemPriceList.Name }}</td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td colspan="2"><label>Item List</label></td>
</tr>
</thead>
<tbody>
<tr>
<td width="575px">${{ itemPriceList.ItemListPrice }}</td>
<td>${{ itemPriceList.ItemListPrice }}</td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td colspan="3"><label>Item Features</label></td>
<td></td>
</tr>
</thead>
<tbody>
<template v-for="item in itemPriceList.ItemFeatures">
<tr v-if="item.FeatureQuantity != 0">
<td width="250px">{{ item.FeatureName }}</td>
<td>{{ item.FeatureQuantity }}</td>
</tr>
</template>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td><label>Item IAM</label></td>
</tr>
</thead>
<tbody>
<tr>
<td>{{ itemPriceList.ItemIAM }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
and here is my code
new Vue({
el: '#app2',
beforeCreate: function () {
StartBusyIndicator("#ItemPriceListWindow");
},
created: function () {
this.GetItemDetails();
},
mounted: function () {
StopBusyIndicator("#ItemPriceListWindow");
},
data: {
itemPriceList: [],
orderItems: currentOrderItems,
currentCatalogName: currentCatalog,
priceList: null
},
methods: {
GetItemDetails: function () {
TryCatch(function() {
let result = GetItemPriceDetails();
this.itemPriceList = result;
priceList = result;
});
},
GetOrderItems: function () {
},
OptionPriceSplitter: function (optionprice) {
return TryCatch(function() {
let sentenceSplit = optionprice.split('& ');
let comp = '';
for (let i = 0; i < sentenceSplit.length; i++) {
comp += sentenceSplit[i] + '\n';
}
return sentenceSplit;
});
},
GlobalListPriceCalculation: function (globalgroupid) {
return TryCatch(function() {
let listPrice = 0.00;
let priceList = this.itemPriceList;
let result = priceList.GlobalListPrices.filter(function(item) {
if (item.GlobalGroupID == globalgroupid) {
listPrice = listPrice + item.Price;
}
});
if (listPrice == 0) {
listPrice = "";
} else {
listPrice = "$" + listPrice.toFixed(2);
}
return listPrice;
});
}
}
});
assuming TryCatch(cb) is something along the lines of
function TryCatch(cb){ let x = null; try { x = cb() } catch(e) {} return x; }
you loose 2 important things:
this (you could bind it via cb.call(this))
utterly usefull error messages
furher points:
I have checked in the console
checkout the excellent browser plugin for ff and chrome vue-devtools
itemPriceList = []
you initalize itemPriceList as array, use it as array item in itemPriceList, but also use it as object {{ itemPriceList.Name }} - what shall it be ?

vueJS hide element in dynamic table row

I have a table that looks like this:
<table class="table">
<thead>
<tr>
<td><strong>Title</strong></td>
<td><strong>Job</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows">
<td><p class="title">My title</p></td>
<td><input type="text" v-model="row.job"></td>
<td><a #click="title()">Remove title</a></td>
</tr>
</tbody>
</table>
Now I wonder how I can toggle a jquery .hide() on the <p class="title">My title</p> when the remove title link is clicked on.
I dont want to use v-show since I am trying to understand how I can target elements within dynamic generated rows in vueJS.
The problem is that there are many rows in my table so every title tag must have a uniqe class and I dont understand how I can hide a specific title on dynamic generated rows
Can be done this way using v-show directive.
new Vue({
el: '.table',
data: {
rows: [
{ showTitle: true, job: 'A' },
{ showTitle: true, job: 'B' },
{ showTitle: true, job: 'C' }
]
}
});
<script src="https://unpkg.com/vue#2.5.2/dist/vue.min.js"></script>
<table class="table">
<thead>
<tr>
<td><strong>Title</strong></td>
<td><strong>Job</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows">
<td><p v-show='row.showTitle' class="title">My title</p></td>
<td><input type="text" v-model="row.job"></td>
<td><a #click="row.showTitle = false">Remove title</a></td>
</tr>
</tbody>
</table>
Edit:
Here is jQuery version but as I already said this is a bad practice.
new Vue({
el: '.table',
data: {
rows: [
{ job: 'A' },
{ job: 'B' },
{ job: 'C' }
]
},
methods: {
hideTitle(index) {
$(this.$refs['title' + index]).hide();
}
}
});
<script src="https://unpkg.com/vue#2.5.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/jquery#3.2.1/dist/jquery.min.js"></script>
<table class="table">
<thead>
<tr>
<td><strong>Title</strong></td>
<td><strong>Job</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows">
<td><p class="title" :ref="'title' + index">My title</p></td>
<td><input type="text" v-model="row.job"></td>
<td><a #click="hideTitle(index)">Remove title</a></td>
</tr>
</tbody>
</table>
Try this one. it works. using v-f and title model
template
<table class="table">
<thead>
<tr>
<td><strong>Title</strong></td>
<td><strong>Job</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows">
<td v-if="title === false"><p class="title">My title</p></td>
<td><input type="text" v-model="row.job"></td>
<td><a #click="title()">Remove title</a></td>
</tr>
</tbody>
</table>
script
data () {
title:false,
},
methods: {
title(){
this.title= true
}
}
As others have mentioned, mixing jQuery and Vue is not the best idea. However, if you must, you can use the event.target that #flypan mentioned along with some jQuery selectors to get what you want.
I put together a JSFiddle using your HTML as an example of what can be done:
new Vue({
el: '#app',
data: {
rows: [
{ job: 'A' }, { job: 'B' }
]
},
methods: {
title: function() {
$title = $(event.target).parent().parent().find("p");
$($title).hide();
}
}
});
You would probably want to tighten the selector to find the "p", but this is just an example. Here's the working JSFiddle:
https://jsfiddle.net/psteele/p5Lpxj5a/
For conditionally displaying, you can use v-show directive,document link here :
v-show doucment.
Hope this can be helpful to you!