Sort data in Vue.js without plugin - javascript

How Sort data in Vue.js without plugin. I have json data like this
var people = [
{
id: 1,
firstName: "John",
lastName: "Doe",
email: "jdoe#example.com",
dob: "12/12/12"
},
{
id: 2,
firstName: "Jane",
lastName: "Smith",
email: "jsmith#example.com",
dob: "11/11/11"
},
{
id: 3,
firstName: "Brian",
lastName: "Rogers",
email: "brogers#example.com",
dob: "10/10/10"
}
];
How to sort them into desc or asc in table and add icon in the table head which is when user click on table head, the icon will be change dynamically.
For example, when data are highest to lowest the icon will be fa-sort-amount-desc
My table code look like this
<div class="row">
<div class="col-xs-12" v-if="!laravelData || laravelData.total === 0"><em>No data available.</em></div>
<div class="col-xs-12">
<div class="table-scrollable">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th> ID</th>
<th> First Name</th>
<th> Last Name</th>
<th> Email </th>
<th> DOB </th>
</tr>
</thead>
<tbody>
<tr v-for="data in laravelData.data" :key="data.title">
<td> <a :href="data.account_link">#{{ data.id }}</a> </td>
<td> #{{ data.firstName }}</td>
<td> #{{ data.lastName }} </td>
<td class="text-right"> #{{ data.email }} </td>
<td class="text-right"> #{{ data.dob }} </td>
</tr>
<tr v-if="!laravelData || laravelData.total === 0">
<td colspan="13"><em>No data available.</em></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

Basically, You need to implement own sorting function to sort the given array of objects, or you can use https://lodash.com/docs/4.17.4#orderBy. By use lodash I created the following simple demo.
var people = [{
id: 10,
firstName: "John",
lastName: "Doe",
email: "jdoe#example.com",
dob: "12/12/12"
}, {
id: 2,
firstName: "Jane",
lastName: "Smith",
email: "jsmith#example.com",
dob: "11/11/11"
}, {
id: 3,
firstName: "Brian",
lastName: "Rogers",
email: "brogers#example.com",
dob: "10/10/10"
}];
new Vue({
el: '#app',
data: {
laravelData: {
data: people,
},
sorting: {
col: '',
type: 'asc'
}
},
methods: {
sort: function(key,type) {
//if( this.sorting.col){
this.sorting.col = key;
this.sorting.type = type
// }
this.laravelData.data = _.orderBy(this.laravelData.data, key, this.sorting.type);
//this.laravelData.data = sortByKey(this.laravelData.data,key)
}
}
})
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<script src="https://unpkg.com/lodash#4.16.0/lodash.js"></script>
<div id="app">
<div class="row">
<div class="col-xs-12" v-if="!laravelData || laravelData.total === 0"><em>No data available.</em></div>
<div class="col-xs-12">
<div class="table-scrollable">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>
ID
<i #click="sort('id','asc')" class="fa fa-sort-asc " aria-hidden="true"></i>
<i #click="sort('id','desc')" class="fa fa-sort-desc" aria-hidden="true"></i>
</th>
<th> First Name</th>
<th> Last Name</th>
<th> Email </th>
<th> DOB </th>
</tr>
</thead>
<tbody>
<tr v-for="data in laravelData.data" :key="data.title">
<td> <a :href="data.account_link">#{{ data.id }}</a> </td>
<td> #{{ data.firstName }}</td>
<td> #{{ data.lastName }} </td>
<td class="text-right"> #{{ data.email }} </td>
<td class="text-right"> #{{ data.dob }} </td>
</tr>
<tr v-if="!laravelData || laravelData.total === 0">
<td colspan="13"><em>No data available.</em></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
You can find jsfiddle # https://jsfiddle.net/sureshamk/zc9wr53g/2/

Related

Vue.JS table data not showing in row

I can't seem to get the data to record into a new row when typed in, and it has since stopped displaying the dummy data when it did before. I really do not understand what I've done wrong so any expert feedback would be greatly appreciated.
new Vue({
el: '#app',
data: {
items: [
{
'id': 1,
'product': 'Mario Kart',
'cost': 39.99
},
{
'id': 2,
'product': 'Call of Duty',
'cost': 129.99
},
{
'id': 3,
'product': 'PS4',
'cost': 169.99
}
],
new_item: [
{
'id': '',
'product': '',
'cost': ''
}
]
},
// calculate total cost of all items
computed: {
result: function() {
return Object.values(this.items).reduce((t, {
cost
}) => t + cost, 0)
},
},
// add and remove items
methods: {
deleteItem: function(index) {
console.log("Removed", index);
this.items.splice(index, 1);
},
addItem: function() {
console.log("Added");
this.items.push({
'id': '',
'items.product': '',
'items.cost.toFixed(2)': ''
});
}
} // close methods
}); // close new Vue
<section class="section">
<div id="app" class="container">
<table class="table is-striped is-fullwidth">
<tr class="th">
<th>Index</th>
<th>Products</th>
<th>Cost</th>
<th></th>
</tr>
<tr class="items" v-for="(item, index) in items" :key="'itm'+index">
<td class="index"> {{ index+1 }} </td>
<td class="service"> {{ items.product }} </td>
<td class="price"> £{{ items.cost }} </td>
<td> <button class="button is-small is-danger" #click='deleteItem(index)'>Delete</button> </td>
</tr>
<tr class="add_new_item" v-for="(new_items, index) in new_item" :key="'new_itm'+index">
<th> </th>
<th> <input class="input is-small" type="text" placeholder="Item Name" v-model="new_items.product"> </th>
<th> <input class="input is-small" type="text" placeholder="The Price" v-model="new_items.cost"> </th>
<th> <button class="button is-info is-small" #click='addItem()'>Add To List</button> </th>
</tr>
<tr class="is-selected">
<td>Total</td>
<td></td>
<td>£{{ result.toFixed(2) }}</td>
<td></td>
</tr>
</table>
</div>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
I corrected several things,
Removing your new_item array it is not needed, you just need to store your input values in your data.
product: '',
cost: '',
lastIndex: 3
lastIndex is initialized to 3 due to you have already 3 items in your data items.
The object to be pushed in the items list and how to get that information.
this.items.push({
'id': this.lastIndex,
'product': this.product,
'cost': parseFloat(this.cost)
});
Here a parseFloat it perform because we get a string for the input. Extra work can be performed to check it is a number, or change the input to allow only numbers.
Removed you for iteration to show the insert new item.
Now it looks:
<tr class="add_new_item">
<th> </th>
<th> <input class="input is-small" type="text" placeholder="Item Name" v-model="product"> </th>
<th> <input class="input is-small" type="text" placeholder="The Price" v-model="cost"> </th>
<th> <button class="button is-info is-small" #click='addItem()'>Add To List</button> </th>
</tr>
and last, Modified how to read the information from the items list
<tr class="items" v-for="(item, index) in items" :key="'itm'+index">
<td class="index"> {{ item.id }} </td>
<td class="service"> {{ item.product }} </td>
<td class="price"> £{{ item.cost }} </td>
<td> <button class="button is-small is-danger" #click='deleteItem(index)'>Delete</button> </td>
</tr>
new Vue({
el: '#app',
data: {
items: [
{
'id': 1,
'product': 'Mario Kart',
'cost': 39.99
},
{
'id': 2,
'product': 'Call of Duty',
'cost': 129.99
},
{
'id': 3,
'product': 'PS4',
'cost': 169.99
}
],
product: '',
cost: '',
lastIndex: 3
},
// calculate total cost of all items
computed: {
result: function() {
return Object.values(this.items).reduce((t, {cost}) => t + cost, 0);
},
},
// add and remove items
methods: {
deleteItem: function(index) {
console.log("Removed", index);
this.items.splice(index, 1);
},
addItem: function() {
console.log("Added");
this.lastIndex += 1;
this.items.push({
'id': this.lastIndex,
'product': this.product,
'cost': parseFloat(this.cost)
});
this.product = '';
this.cost = '';
}
} // close methods
}); // close new Vue
<section class="section">
<div id="app" class="container">
<table class="table is-striped is-fullwidth">
<tr class="th">
<th>Index</th>
<th>Products</th>
<th>Cost</th>
<th></th>
</tr>
<tr class="items" v-for="(item, index) in items" :key="'itm'+index">
<td class="index"> {{ item.id }} </td>
<td class="service"> {{ item.product }} </td>
<td class="price"> £{{ item.cost }} </td>
<td> <button class="button is-small is-danger" #click='deleteItem(index)'>Delete</button> </td>
</tr>
<tr class="add_new_item">
<th> </th>
<th> <input class="input is-small" type="text" placeholder="Item Name" v-model="product"> </th>
<th> <input class="input is-small" type="text" placeholder="The Price" v-model="cost"> </th>
<th> <button class="button is-info is-small" #click='addItem()'>Add To List</button> </th>
</tr>
<tr class="is-selected">
<td>Total</td>
<td></td>
<td>£{{ result }}</td>
<td></td>
</tr>
</table>
</div>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
No need to create a new object for the New items and loop through it. I have modified your code. please refer below
new Vue({
el: '#app',
data: {
items: [
{
'id': 1,
'product': 'Mario Kart',
'cost': 39.99
},
{
'id': 2,
'product': 'Call of Duty',
'cost': 129.99
},
{
'id': 3,
'product': 'PS4',
'cost': 169.99
}
],
currentItemId: 0,
product: '',
cost: ''
},
// calculate total cost of all items
computed: {
result: function() {
return Object.values(this.items).reduce((t, {
cost
}) => t + cost, 0)
},
},
created: function() {
this.currentItemId = this.items.length + 1;
},
// add and remove items
methods: {
deleteItem: function(index) {
console.log("Removed", index);
this.items.splice(index, 1);
},
addItem: function() {
console.log("Added");
this.items.push({
'id': this.currentItemId++,
'product': this.product,
'cost': this.cost
});
this.product = '';
this.cost = '';
}
} // close methods
}); // close new Vue
<section class="section">
<div id="app" class="container">
<table class="table is-striped is-fullwidth">
<tr class="th">
<th>Index</th>
<th>Products</th>
<th>Cost</th>
<th></th>
</tr>
<tr class="items" v-for="(item, index) in items" :key="index">
<td class="index"> {{ index+1 }} </td>
<td class="service"> {{ item.product }} </td>
<td class="price"> £{{ item.cost }} </td>
<td> <button class="button is-small is-danger" #click='deleteItem(index)'>Delete</button> </td>
</tr>
<tr class="add_new_item">
<th> </th>
<th> <input class="input is-small" type="text" placeholder="Item Name" v-model="product"> </th>
<th> <input class="input is-small" type="text" placeholder="The Price" v-model="cost"> </th>
<th> <button class="button is-info is-small" #click='addItem()'>Add To List</button> </th>
</tr>
<tr class="is-selected">
<td>Total</td>
<td></td>
<td>£{{ result }}</td>
<td></td>
</tr>
</table>
</div>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Binding and using an object's fields within an object returned by the server API with a PrimeNG data table

Good day
Currently I am returning an array of objects looking like:
{
attendeeCount: 5
bookDay: "2018-11-22T14:06:24.120Z"
bookingComment: "This is a test"
conferenceRoom: {id: 8, name: "Main Boardroom", seatingCount: 10, location: "Site Office", projector: "YES"}
employee: {id: 111, title: "Mr.", initials: "J", preferredName: "John", lastName: "Smith", …}
id: 1
refreshment: {id: 1, name: "Coffee, Tea and Water"}
timeSlot: "07:00 - 07:30"
}
The requirement then is that I should be able to render the PrimeNG data table using TypeScript like the below:
public getRoomRosterTable() {
this.conferenceRoomBookingService.getRoomRoster(this.dateValue, this.selectedConferenceRoom.id).subscribe(response => {
console.warn(response);
this.conferenceRoomBookings = response;
}, error1 => {
this.alertService.error(error1);
});
this.timeSlotCols = [
{field: 'timeSlot', header: 'Time Slot'},
{field: 'employee.preferredName' + 'employee.lastName', header: 'Slot Booked By'},
{field: 'attendeeCount', header: 'Attendee Count'},
{field: 'refreshment.name', header: 'Refreshment Details'},
{field: 'bookingComment', header: 'Booking Comment'}
];
}
Combined with html looking like:
<p-table [value]="conferenceRoomBookings" [reorderableColumns]="true" [columns]="timeSlotCols">
<ng-template pTemplate="header" let-columns>
<tr>
<th *ngFor="let col of columns">
<div style="text-align:center">
{{col.header}}
</div>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-rowData let-columns="columns">
<tr>
<td *ngFor="let col of columns">
{{rowData[col.field]}}
</td>
</tr>
</ng-template>
</p-table>
This however only renders the columns that have direct data bound. I cannot seem to get the table to pick up properties of nested objects.
Is the above possible currently with PrimeNG, or do I need to create a custom DTO on the server returning only 'direct' fields for the PrimeNG table?
Could not get this to work with the PrimeNG table and resorted to using *ngFor combined with *ngIf wrapped in divs to detect nulls:
<table class="table-bordered">
<thead>
<tr>
<th>
<div style="align-content: center">
Time Slot
</div>
</th>
<th>
<div style="align-content: center">
Booked By
</div>
</th>
<th>
<div style="align-content: center">
Attendee Count
</div>
</th>
<th>
<div style="align-content: center">
Refreshment Requirement
</div>
</th>
<th>
<div style="align-content: center">
Booking Details
</div>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let conferenceRoomBooking of conferenceRoomBookings">
<td>
<div *ngIf="conferenceRoomBooking.timeSlot">
{{conferenceRoomBooking.timeSlot}}
</div>
</td>
<td>
<div *ngIf="conferenceRoomBooking.employee">
{{conferenceRoomBooking.employee.preferredName}} {{conferenceRoomBooking.employee.lastName}}
</div>
</td>
<td>
<div *ngIf="conferenceRoomBooking.attendeeCount">
{{conferenceRoomBooking.attendeeCount}}
</div>
</td>
<td>
<div *ngIf="conferenceRoomBooking.refreshment">
{{conferenceRoomBooking.refreshment.name}}
</div>
</td>
<td>
<div *ngIf="conferenceRoomBooking.bookingComment">
{{conferenceRoomBooking.bookingComment}}
</div>
</td>
</tr>
</tbody>
</table>

unable to select all check on ngonit using angular

here i am trying to select all check boxes by using ngOnit but it is calling that function and by clicking it is calling the function then it is selecting all
.html code
<div>
<h2>Hello Angular2</h2>
<table class="table table-bordered table-condensed table-striped table-hover">
<thead>
<tr>
<th></th>
<th>Size</th>
<th>Diameter</th>
<th class="text-center">
<input type="checkbox" name="all" [checked]="isAllChecked()" (change)="checkAll($event)"/>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let size of sizes ; let i = index">
<td class="text-right">{{i + 1}}</td>
<td class="text-right">{{size.size}}</td>
<td>{{size.diameter}}</td>
<td class="text-center">
<input type="checkbox" name="sizecb[]" value="{{size.id}}" [(ngModel)]="size.state"/>
</td>
</tr>
</tbody>
</table>
</div>
.ts code:
sizes: any[] = [
{ 'size': '0', 'diameter': '16000 km' },
{ 'size': '1', 'diameter': '32000 km' }
];
checkAll(ev) {
this.sizes.forEach(x => x.state = ev.target.checked)
}
isAllChecked() {
debugger;
return this.sizes.every(_ => _.state);
}
ngOnInit() {
this.isAllChecked();
}
below is my stack blitz url
https://stackblitz.com/edit/angular-pzvusr
your code is working fine. missing thing is you don't have a key state: true in your defined array. that's why it is not selecting all checkbox.
try this array
sizes: any[] = [
{ 'size': '0', 'diameter': '16000 km', 'state': true },
{ 'size': '1', 'diameter': '32000 km', 'state': true }
];
<div>
<h2>Hello Angular2</h2>
<table class="table table-bordered table-condensed table-striped table-hover">
<thead>
<tr>
<th></th>
<th>Size</th>
<th>Diameter</th>
<th class="text-center">
<input type="checkbox" name="all" [checked]="isAllChecked()" (change)="checkAll($event)"/>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let size of sizes ; let i = index">
<td class="text-right">{{i + 1}}</td>
<td class="text-right">{{size.size}}</td>
<td>{{size.diameter}}</td>
<td class="text-center">
<input type="checkbox" name={{sizecb + i}} value="{{size.id}}"
(change)="sizes[i].state = !sizes[i].state" [checked]="size.state" [(ngModel)]="size.state"/>
</td>
</tr>
</tbody>
</table>
</div>
and in ts
try this array
sizes: any[] = [
{ 'size': '0', 'diameter': '16000 km', 'state': true },
{ 'size': '1', 'diameter': '32000 km', 'state': true }
];

VueJS Accordion Table - Appears outside of the table

I have a table where the data is fetched using ajax. I'm trying to have a table where each row has an associated hidden row and clicking on the row toggles the display of the hidden row. The hidden row contains an accordion.
The problem is that the accordion is getting all messed up and shows at the bottom of the table, rather than showing below the particular row that it was clicked on.
My code is as follows:
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th v-for="column in columns">
<span v-if="column == 'Predictive Price'">
{{column}}
<i class="fa fa-info-circle" v-tooltip="msg"></i>
</span>
<span v-else-if="column == 'Actual Price'">
{{column}}
<i class="fa fa-info-circle" v-tooltip="tooltip.actual_price"></i>
</span>
<span v-else>
{{column}}
</span>
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="row in model" #click="showRow">
<td>
{{row.id}}
</td>
<td>
{{row.company_customer.customer_name}}
</td>
<td>
<i class="fa fa-map-marker"></i>
<small>
{{row.pickup_addr.address_1}}, {{row.pickup_addr.city}}, {{row.pickup_addr.postcode}}
</small>
</td>
<td>
<i class="fa fa-map-marker"></i>
<small>
{{row.pickup_addr.address_1}}, {{row.pickup_addr.city}}, {{row.pickup_addr.postcode}}
</small>
</td>
<td>
£{{row.predictive_price}}
</td>
<td>
£{{row.actual_price}}
</td>
<td>
n/a
</td>
<tr>
<td colspan="7" v-if="contentVisible">
<div class="accordian-body">
ACCORDION
</div>
</td>
</tr>
</tr>
</tbody>
</table>
<script>
export default {
methods: {
data() {
return {
msg: 'This is just an estimation!',
tooltip: {
actual_price: 'Click on the price to edit it.'
},
contentVisible: false,
}
},
rowRow() {
this.contentVisible = !this.contentVisible;
}
}
}
</script>
Where can I place the accordion div in order for it to display correctly?
EDIT:
Please see fiddle: https://jsfiddle.net/49gptnad/355/
It sounds like you want an accordion associated with every row, so really, you want two rows for each item of your data.
You can accomplish that by moving your v-for to a template tag that wraps both of your rows.
Additionally, you need to control whether content is visible on a row by row basis, so add a contentVisible property to each data item and use it to control whether your second row is visible or not.
console.clear()
var vm = new Vue({
el: '#vue-instance',
data: {
testing: [{
id: 1,
name: "Customer 1",
contentVisible: false
},
{
id: 2,
name: "Customer 1",
contentVisible: false
},
{
id: 3,
name: "Customer 3",
contentVisible: false
},
],
columns: ["id", "name"]
},
mounted() {
console.log(this.testing);
},
methods: {
showRow(data) {
this.contentVisible = !this.contentVisible;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="vue-instance">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th v-for="column in columns">
{{column}}
</th>
</tr>
</thead>
<tbody>
<template v-for="row in testing">
<tr #click="row.contentVisible = !row.contentVisible">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
</tr>
<tr v-if="row.contentVisible">
<td :colspan="columns.length" >
<div class="accordian-body">
afasfafs
</div>
</td>
</tr>
</template>
</tbody>
</table>
</div>
Here is your updated fiddle.

vuejs v-if condition for input model

i m just learn vue.js
and i want to display a table data.my opinion is when display mode the table just show. and when i click edit button of the line of the table, i want this line convert to model.
this is my code:
```
<table class="table table-bordered">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>pass</th>
<th>action</th>
</tr>
</thead>
<tbody>
<template v-for="data in apidata" track-by="$index">
<tr>
<td>{{$index + 1}}</td>
<td>
<div v-show="data.editmode"><input v-model="data.name"></div>
<div v-else>{{data.name}}</div>
</td>
<td>
<div v-if=data.editmode><input v-model="data.name"></div>
<div v-else>{{data.name}}</div>
</div>
</td>
<td>
<button v-on:click="remove($index)" class="btn btn-danger">remove</button>
<button v-on:click="edit(data)" class="btn btn-danger">edit</button>
</td>
</tr>
</template>
</tbody>
</table>
```
my data is like this
[{name:'test', pass:'1'}, {name:'test2', pass:'2'}]
i bind a edit()function to listen the click event.
edit: function(data){
alert(data.editmode);
data.editmode = true;
}
i think when i click .becuase the data.editmode will change to true.
this line will convert to input mode . but its useless.
i have tried v-if=data.editmode , v-if="data.editmode" ,v-show="data.editmode" , still got nothing
i dnt know why?
You just need to include editmode in your data declaration so that it is a reactive data item.
new Vue({
el: 'body',
data: {
apidata: [{
name: 'test',
pass: '1',
editmode: false
}, {
name: 'test2',
pass: '2',
editmode: false
}]
},
methods: {
edit: function(data) {
data.editmode = !data.editmode;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.27/vue.min.js"></script>
<table class="table table-bordered">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>pass</th>
<th>action</th>
</tr>
</thead>
<tbody>
<tr v-for="data in apidata">
<td>{{$index + 1}}</td>
<td>
<div v-if="data.editmode">
<input v-model="data.name">
</div>
<div v-else>{{data.name}}</div>
</td>
<td>
<div v-if=data.editmode>
<input v-model="data.pass">
</div>
<div v-else>{{data.pass}}</div>
</td>
<td>
<button v-on:click="remove($index)" class="btn btn-danger">remove</button>
<button v-on:click="edit(data)" class="btn btn-danger">edit</button>
</td>
</tr>
</tbody>
</table>

Categories