Dynamically calculate percentage in a datatable - datatables

How can I dynamically calculate a percentage in a datatable when a search is applied ?
The result should be like this :
<tr>
<td>10%</td><td>100</td>
<td>90%</td><td>900</td>
</tr>
The percentage should be based on the total of rows shown.

The following example shows you a basic approach.
The end result looks like this - note you may get a small rounding error when summing the percentages:
The code is as follows:
UPDATE - There was a problem with my original solution - it used jQuery to populate DataTable cells with the calculated percentages. That meant DataTables did not respond to sorting/filtering for the percentages column. It's better to use DataTables functions to populate the data cells.
<body>
<div style="margin: 20px;">
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr><th>ID</th><th>Amount</th><th>Percent</th></tr>
</thead>
<tbody>
<tr><td>1</td><td>123.4</td><td class="percent"></td></tr>
<tr><td>2</td><td>234.5</td><td class="percent"></td></tr>
<tr><td>3</td><td>543.21</td><td class="percent"></td></tr>
<tr><td>4</td><td>76</td><td class="percent"></td></tr>
<tr><td>5</td><td>87</td><td class="percent"></td></tr>
</tbody>
</table>
</div>
<script type="text/javascript">
$(document).ready(function() {
var table = $('#example').DataTable({
// wait for table and data to finish being initialized:
"initComplete": function( settings, json ) {
populatePercentages();
}
});
function populatePercentages() {
var total = 0.0;
// first calcuate the total:
$('#example').DataTable().rows().every( function ( rowIdx, tableLoop, rowLoop ) {
// assumes amounts are not null, all numeric, and are in column 2:
total = total + Number(this.data()[1]);
});
// then calculate the percentage for each row:
$('#example').DataTable().rows().every( function ( rowIdx, tableLoop, rowLoop ) {
var amount = this.data()[1];
// calculate percent to 2 decimal places:
var percentage = Math.round((amount / total) * 10000) / 100;
var cells = $('#example').DataTable().cells(rowIdx, 2);
cells.every( function () {
this.data(percentage);
});
});
}
});
</script>
</body>
Points to note:
1) It uses a "percent" class for the cells where percentages will be shown. This makes selection easier.
2) You can do something similar as (1) for the amounts cells also. I did not, here.
3) The code loops through the table data twice: Once to calculate the grand total amount; and again to calculate the percentages.
4) You would need to adjust assumptions about column offsets to fit your table design.
5) There is more validation which should really be added, to handle missing or non-numeric data, to make the code less brittle.
Handling the case when a search is applied:
Filtering:
The above solution can be enhanced to re-calculate percentages when data is filtered.
1) There are two places where the rows() function is used. In both places change this to the following: rows( { search: 'applied' } ). This will ensure only visible rows are considered when percentages are calculated.
2) Add a new section of code to handle keyup events for the search (filter) box. In my case the selector for this input control is as shown below - but this will need to be edited for other datatables with different IDs (my example table's ID is example):
<script type="text/javascript">
$(document).ready(function() {
var table = $('#example').DataTable({
...
});
function populatePercentages() {
...
}
// catch filter events and re-calculate percentages accordingly:
$('div#example_filter.dataTables_filter > label > input').keyup(function (e) {
populatePercentages();
});
});
</script>
With these changes, a filtered list will look like this:

Related

Totaling a Summary Line in Data Table

I hve a sumField method which totals values in a given column:
sumField (key) {
let total = 0
const sum = this.tableName.reduce((acc, cur) => {
return (total += +cur[key])
}, 0)
return sum
}
Inside my data table I call sumField to produce a rolling total of the values in a specific column of my data table:
<template v-slot:[`body.append`]="{headers}">
<tr class="summary">
<td v-for="(header,i) in headers" :key="i">
<div v-if="header.value == 'COL HEADER 1'">
{{ sumField('COL HEADER 1') }}</div>
<div v-else-if="header.value == 'COL HEADER 2'">
{{ sumField('COL HEADER 2') }}</div>
</td>
</tr>
</template>
This is presented on screen as an additional line of the data table, and the values change depending on the filters applied to the table.
Is there a way to sum the values calculated, and show this as a rolling total value also?
Got a solution which I hope might provide some help to others in the future!
Started by creating a new function:
sumTot (col1, col2) {
var one = col1
var two = col2
var tot = col1 + col2
return tot
}
Then I called sumTot giving the arguments of the individual sumField calls:
{{ sumTotal(sumField ('COL HEADER 1'), sumField ('COL HEADER 2')) }}
That gives me a dynamically updated running total value.

Filtering Data Table in PrimeNG

How can I get the number of rows after filtering using PrimeNG's default filters in data table.
[totalRecords]="totalRecords" always shows the records count which is fetched initially.
Even though after filtering, totalRecords value remains same and does not change after filtering.
Example:
initially totalRecords is 50 and after filtering, no.of records it shows in data table is 15. But I cannot get the value 15, instead getting 50.
Is there any way ?
Supposing you have a reference to your datatable component, just ask totalRecords property on it :
<p-dataTable [value]="cars" [rows]="10" [paginator]="true" [globalFilter]="gb" #dt>
...
</p-dataTable>
{{ dt.totalRecords }} records
Demo
The above answer is correct and I'm adding up a little thing to it.
If you want to bind the totalRecords value to your typescript .ts file, then use an (onFilter) event and trigger a function with parameters as $event and dt.totalRecords
In my case, i have given
<p-table #dt [value]="personListData" [columns]="columns" (onPage)="onPageChange($event)" [resizableColumns]="true" [paginator]="true" [rows]="rowsCount" selectionMode="multiple" [(selection)]="selected_data" [loading]="loading" [totalRecords]="totalRecords" class="table table-hover table-responsive table-bordered" [responsive]="true" (onFilter)="handleFilter($event,dt.totalRecords)">
In short,
(onFilter)="handleFilter($event,dt.totalRecords)"
Function in .ts file ,
handleFilter(e,filteredRecordCount){
console.log("filteredRecordCount");
}
NOTE: If you want to use the filtered records count value, then you
can assign it to any variable and use anywhere in your typescript
file.
I'm on Angular 8, and my table is not paginated. dt.totalRecords is always the full amount. So if I have 20 rows, and I get it filtered down to 2 on-screen, dt.totalRecords still = 20.
What I ended up doing was using the onFilter, passing in the entire dt, then using dt.filteredValue:
onFilter(event: any, table: any): void {
if(!!table.filteredValue) {
this.visibleRows$.next(table.filteredValue.length);
}
}
You have to check for null, because if you change the filter but don't filter out any additional rows, filteredValue is null.
html:
<p-dataTable #dt (onFilter)="handleFilter()" [value]="cars" [rows]="10" [paginator]="true" >
...
</p-dataTable>
{{ dt.totalRecords }} records
ts:
#ViewChild('dt', { static: true }) dt: Table;
handleFilter() {
if (this.dt.filteredValue != null)
this.dt.totalRecords = this.dt.filteredValue.length;
else
this.dt.totalRecords = this.cars.length;
}

Get 'data-sort' orthogonal value from DataTables within search.push function

I am looping the rows within $.fn.dataTable.ext.search.push function to select some rows based on many criteria. I am setting some values on the TD of the tables known as orthogonal data. I am trying to get the value of the 'data-sort' but I don't know how. I am able to get the cell's inner data via data[2] (for column 2), but not the 'data-sort' or 'data-filter'. Any ideas?
$.fn.dataTable.ext.search.push(
function (settings, data, dataIndex) {
var iRating = parseFloat(data[2]) || 0; // this works
var datasort = //somehow get the data-sort from the TD
);
HTML
<td data-sort="57000" class=" seqNum">.....</td>
Looks like this way I can get the value. If there are any other better ways please advice:
$(settings.aoData[dataIndex].anCells[2]).data('sort')
Yes there is an easier way.
The data parameter is the "search data" for the row. i.e. the values for "filter" data for each col - not your "sort" data / anything else.
But you also have access to the full data object for the row i.e. the 4th parameter - rowData.
So you need to use rowData at the index of the column you want (you say 'for column 2', zero based so - 2 would be for the 3rd column).
Then you should find a property called 'sort' :
function(settings, searchData, index, rowData, counter){
var dataSort = rowData[1]['sort'];
console.log(`This should be the value you want : ${dataSort}`);
}
As per the documentation here

How to traverse through Dynamic Object of Arrays in VueJS

I have an object with arrays which is nothing but JSON reply from server which I converted into Object and now it looks like this (but lot of values into it):-
Object_return:{
name:[1,25,2,24,3,78],
age:[2,34,4,78]
}
here name and age is dynamic coming from the server, so I do not know what exact values coming there so I can not refer it while iterating through the for loop
<th v-for = "item in Object_return.name">
and also I want to show this in a DataTable so the first row should looks like this
------------------
1 25
-------
name 2 24
-------
3 78
--------------------
second row
---------------------
2 34
-------
age 4 78
------------------------
and so on and so forth for all the values coming from the server
Does someone have an idea how to do this
You can iterate over an object and get the key value as the second argument.
<tr v-for="val, key in Object_return">
Here, key will be the name of the property.
Then, since you want to group the arrays in pairs, I suggest a computed property to massage the data into the format you want.
Here is a working example.
console.clear()
new Vue({
el: "#app",
data:{
serverData: {
name:[1,25,2,24,3,78],
age:[2,34,4,78]
}
},
computed:{
massaged(){
return Object.keys(this.serverData).reduce((acc, val) => {
// split each array into pairs
const copy = [...this.serverData[val]]
let pairs = []
while (copy.length > 0)
pairs.push(copy.splice(0, 2))
// add the paired array to the return object
acc[val] = pairs
return acc
}, {})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<table>
<tr v-for="val, key in massaged">
<td>{{key}}</td>
<td>
<table>
<tr v-for="pair in val">
<td>{{pair[0]}}</td>
<td>{{pair[1]}}</td>
</tr>
</table>
</td>
</tr>
</table>
</div>

Srpy dataset if decision based on value from different dataset

Many thanks for reading this.
I have asked as well in Adobe forums with no luck.
I am building a small library application for school books.
I have created a database with lets say 2 tables
Books ( ID_Book , Writer , Title , Copies) and
Loans ( ID_Book , Load_ID , Loan_Date ) etc
I have used correctly spry to create easily a table which print the book list in a table
with pagination .
var ds1 = new Spry.Data.XMLDataSet("ajaxquery.php", "root/row", {sortOnLoad: "Writer", sortOrderOnLoad: "ascending"});
ds1.setColumnType("ID_Book", "number");
var pv1 = new Spry.Data.PagedView( ds1 ,{ pageSize:10 });
var pv1PagedInfo = pv1.getPagingInfo();
pv1.setColumnType("ID_Book", "number");
I have made the necessary declarations to produce the dataset for the Loans
var ds3 = new Spry.Data.XMLDataSet("ajaxallloans", "root/row", {sortOnLoad: "ID_Book", sortOrderOnLoad: "ascending"});
ds3.setColumnType("ID_Book", "number");
ds3.setColumnType("ID_Dan", "number");
I would like to find a way to change the table row color for the BOOKS table IF an ID_Book is within the Loans table - ds3.
The table is created
<div spry:region="pv1" id="bibliapv">
<div spry:state="loading" class="loading" >Loading...</div>
<div spry:state="ready">
<table>
<tr >
<th width="75" spry:sort="ID_Book"> Book No</th>
<th width="123" spry:sort="Writer">Writer </th>
etc...
</tr>
<tr spry:repeat="pv1" spry:select="mySelectClass" spry:hover="hover">
<td >{ID_Book}</td>
<td>{writer}</td>
etc ..
</tr>
</table>
</div>
</div>
<div>
Many thanks again.
Dinos - Greece
Many thanks again for reading .
I found a solution based on the ideas drawn from
labs.adobe.com/technologies/spry/samples/data_region/CustomColumnsSam ple.html
I have added the following code:
created a css rule
lets say
.match {
background-color: #0CF;
}
In spry:region add the class {cssrule} which is added dynamically shortly after <tr class="{cssrule}" spry:repeat="pv1" spry:select="mySelectClass" spry:hover="hover">
3.
Then just before closing tag added (you could put it earlier in code)
<script type="text/javascript">
ds2.addObserver({
onPostLoad:function( ds2, data ){
var data = ds2.getData();
var pv1data = pv1.getData();
for( var i = 0; i < pv1data.length; i++ )
{
for (var j =0 ; j< data.length ; j++)
{ if ((data[j].Writer).toString()== (pv1data[i].Writer).toString() ) //or whatever you like!
{pv1data[i].cssrule="match"; }
}
}
}
});
</script>