Building table dynamically with PDFMake - javascript

I'm working with pdfmake to generate pdf with javascript. I'm trying to build a table dynamically but not works ,this my attempt
$.ajax({
type: "POST",
url: myURL,
success:function(data){
/* data has a format like :
*[{"peaje":"Peaje 1","ruta":"Ruta 1","fechaCruce":"2014-10-18","hora":"15:42","valor":"5000"},{"peaje":"Peaje 1","ruta":"Ruta 1","fechaCruce":"2014-10-18","hora":"14:21","valor":"7000"},{"peaje":"Peaje 1","ruta":"Ruta 1","fechaCruce":"2014-09-19","hora":"11:58","valor":"17000"}]
*/
var peajes = JSON.parse( data );
var body = [];
var titulos = new Array( 'PEAJE', 'RUTA', 'FECHA CRUCE', 'HORA', 'VALOR' );
body.push( titulos );
for (key in peajes)
{
if (peajes.hasOwnProperty(key))
{
var peaje = peajes[key];
var fila = new Array();
fila.push( peaje.peaje.toString() );
fila.push( peaje.ruta.toString() );
fila.push( peaje.fechaCruce.toString() );
fila.push( peaje.hora.toString() );
fila.push( peaje.valor.toString() );
body.push(fila);
}
}
console.log( body );
var docDefinition = {
content: [
{
table: {
headerRows: 1,
widths: [ '*', 'auto', 100, '*' ],
body: body
}
}]
};//end docDefinition
pdfMake.createPdf(docDefinition).open();
}//end success
});
This is the example of the library http://pdfmake.org/#/gettingstarted
I don't know what am I doing wrong?

For multiple rows, here is an example
var externalDataRetrievedFromServer = [
{ name: 'Bartek', age: 34 },
{ name: 'John', age: 27 },
{ name: 'Elizabeth', age: 30 },
];
function buildTableBody(data, columns) {
var body = [];
body.push(columns);
data.forEach(function(row) {
var dataRow = [];
columns.forEach(function(column) {
dataRow.push(row[column].toString());
})
body.push(dataRow);
});
return body;
}
function table(data, columns) {
return {
table: {
headerRows: 1,
body: buildTableBody(data, columns)
}
};
}
var dd = {
content: [
{ text: 'Dynamic parts', style: 'header' },
table(externalDataRetrievedFromServer, ['name', 'age'])
]
}

You should make array of column names & values:
var column = [];
column.push({ text: 'A', style: 'tableHeader'});
column.push({ text: 'B', style: 'tableHeader'});
var value = [];
value.push({ text: 'Asda', style: 'tableHeader'});
value.push({ text: 'Bsa', style: 'tableHeader'});
When you make a table, you should do like this.
table: {
headerRows: 1,
body: [
column, value
]
}

For headers and rows are dynamic , we can define headers in an array and also rows and then join them into one following this example ( copy and paste into http://pdfmake.org/playground.html to see it in action ) :
// playground requires you to assign document definition to a variable called dd
var headers = {
fila_0:{
col_1:{ text: 'Faltas', style: 'tableHeader',rowSpan: 2, alignment: 'center',margin: [0, 8, 0, 0] },
col_2:{ text: 'Fecha', style: 'tableHeader',rowSpan: 2, alignment: 'center',margin: [0, 8, 0, 0] },
col_3:{ text: 'Descripción', style: 'tableHeader',rowSpan: 2, alignment: 'center',margin: [0, 8, 0, 0] },
col_4:{ text: 'Cita con acudientes', style: 'tableHeader',colSpan: 2, alignment: 'center' }
},
fila_1:{
col_1:{ text: 'Header 1', style: 'tableHeader', alignment: 'center' },
col_2:{ text: 'Header 2', style: 'tableHeader', alignment: 'center' },
col_3:{ text: 'Header 3', style: 'tableHeader', alignment: 'center' },
col_4:{ text: 'Citación', style: 'tableHeader', alignment: 'center' },
col_5:{ text: 'Cumplimiento', style: 'tableHeader', alignment: 'center'}
}
}
var rows = {
a: {
peaje: '1',
ruta: '2',
fechaCruce: '3',
hora: '4',
valor: '5'
},
b: {
peaje: '1',
ruta: '2',
fechaCruce: '3',
hora: '4',
valor: '5'
}
}
var body = [];
for (var key in headers){
if (headers.hasOwnProperty(key)){
var header = headers[key];
var row = new Array();
row.push( header.col_1 );
row.push( header.col_2 );
row.push( header.col_3 );
row.push( header.col_4 );
row.push( header.col_5 );
body.push(row);
}
}
for (var key in rows)
{
if (rows.hasOwnProperty(key))
{
var data = rows[key];
var row = new Array();
row.push( data.peaje.toString() );
row.push( data.ruta.toString() );
row.push( data.fechaCruce.toString() );
row.push( data.hora.toString() );
row.push( data.valor.toString() );
body.push(row);
}
}
var dd = {
pageMargins: [40,155,40,55],
pageOrientation: 'landscape',
header: function() {
return {
margin: 40,
columns: [
{
},
{ text:['Resumen disciplinario'],
alignment: 'left',bold:true,margin:[-405,80,0,0],fontSize: 24}
]
}
},
footer: function(currentPage, pageCount) {
return { text:'Pagina '+ currentPage.toString() + ' de ' + pageCount, alignment: 'center',margin:[0,30,0,0] };
},
content: [
//{ text: 'Tables', style: 'header' },
'\nEl estudiante AGRESOTH NEGRETE JORYETH TATIANA - 901 - TARDE tiene 1 actas, con 1 faltas acomuladas y a manera de resumen descritas a continuación:\n\n',
//{ text: 'A simple table (no headers, no width specified, no spans, no styling)', style: 'sta' },
//'The following table has nothing more than a body array',
{
style: 'tableExample',
table: {
widths: [ '*', '*', '*', '*', '*' ],
headerRows: 2,
// keepWithHeaderRows: 1,
body: body
}
}],
styles: {
header: {
fontSize: 28,
bold: true
},
subheader: {
fontSize: 15,
bold: true
},
quote: {
italics: true
},
small: {
fontSize: 8
},
sta: {
fontSize: 11,
bold: false,
alignment: 'justify'
}
}
}

Related

Simplify JavaScript array variable

I'm looking to simplify this code. Any way to it so? Spring MVC + Apex Charts
var d = /*[[${s0}]]*/ null`; <-- It is sent via the Spring Framework. Basically represents datetime(in millis) at `d[0]`, `d[3]`,... Temperature at `d[1]`, `d[4]`,... and Humidity at `d[2]`, `d[5]`,...
<script type="text/javascript" th:inline="javascript">
var d = /*[[${s0}]]*/ null;
var options = {
chart: {
type: 'area',
height: 300
},
series: [
{
name: 'Temperature',
data: [
[d[0], d[1]],
[d[3], d[4]],
[d[6], d[7]],
[d[9], d[10]],
[d[12], d[13]],
[d[15], d[16]],
[d[18], d[19]],
[d[21], d[22]],
[d[24], d[25]],
[d[27], d[28]],
[d[30], d[31]],
[d[33], d[34]],
[d[36], d[37]],
[d[39], d[40]],
[d[42], d[43]],
[d[45], d[46]],
[d[48], d[49]],
[d[51], d[52]],
[d[54], d[55]],
[d[57], d[58]],
[d[60], d[61]],
[d[63], d[64]],
[d[66], d[67]],
[d[69], d[70]]
]
},
{
name: "Humidity",
data: [
[d[0], d[2]],
[d[3], d[5]],
[d[6], d[8]],
[d[9], d[11]],
[d[12], d[14]],
[d[15], d[17]],
[d[18], d[20]],
[d[21], d[23]],
[d[24], d[26]],
[d[27], d[29]],
[d[30], d[32]],
[d[33], d[35]],
[d[36], d[38]],
[d[39], d[41]],
[d[42], d[44]],
[d[45], d[47]],
[d[48], d[50]],
[d[51], d[53]],
[d[54], d[56]],
[d[57], d[59]],
[d[60], d[62]],
[d[63], d[65]],
[d[66], d[68]],
[d[69], d[71]]
]
}
],
xaxis: {
type: 'datetime'
},
yaxis: [
{
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Temperature"
}
}, {
min: 0,
max: 100,
opposite: true,
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Humidity"
}
}
],
legend: {
position: 'top',
horizontalAlign: 'center'
},
tooltip: {
x: {
format: 'HH:mm dd/MM/yy'
},
}
}
var chart = new ApexCharts(document.querySelector("#chart0"), options);
chart.render();
</script>
I just need to simplify sending data via d[0], d[1] etc. Is there any kind of loop or anything else I can use?
You could take a function which takes the data and a pattern for the wanted elements and an offset for increment for the next row.
function mapByPattern(data, pattern, offset) {
var result = [], i = 0;
while (i < data.length) {
result.push(pattern.map(j => data[i + j]));
i += offset;
}
return result;
}
var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
result = { series: [
{ name: 'Temperature', data: mapByPattern(data, [0, 1], 3) },
{ name: "Humidity", data: mapByPattern(data, [0, 2], 3) }
]};
console.log(result);
Thank You, Nina. Code code didn't work exactly as i wanted but was so helpful to fix my own. Thanks alot! Here's some fixed code :)
var data = /*[[${s0}]]*/ null;
function mapByPattern(data, pattern, offset) {
var result = [], i = 0;
while (i < data.length) {
result.push(pattern.map(j => data[i + j]));
i += offset;
}
return result;
}
var options = {
chart: {
type: 'area',
height: 300
},
series: [
{
name: 'Temperature',
data: mapByPattern(data, [0, 1], 3)
},
{
name: "Humidity",
data: mapByPattern(data, [0, 2], 3)
}
],
xaxis: {
type: 'datetime'
},
yaxis: [
{
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Temperature"
}
}, {
min: 0,
max: 100,
opposite: true,
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Humidity"
}
}
],
legend: {
position: 'top',
horizontalAlign: 'center'
},
tooltip: {
x: {
format: 'HH:mm dd/MM/yy'
},
}
}
var chart = new ApexCharts(document.querySelector("#chart0"), options);
chart.render();

Angularjs - html to pdf using pdfmake.js

Using pdfmake.js to generate pdf in javascript. But it generates a blank document if the content is too large. Used the html2canvas to create the canvas and created the pdf using this. How can resolve this issue??
self.exportAsCanvas = function (contentObject, fileName, heading) {
var useWidth = $(contentObject)[0].offsetWidth;
var useHeight = $(contentObject)[0].offsetHeight;
//var graphContent = angular.element(contentObject).find('.graph-content');
html2canvas(contentObject, {
onrendered: function (canvas) {
document.body.appendChild(canvas);
var data = canvas.toDataURL();
var docDefinition = {
header: { text: heading, style: 'header' },
footer: {
columns: [
{ text: 'Copyright © 2015 H&R Block. All Rights Reserved.', alignment: 'center',fontSize: 11 }
]
},
pageOrientation: self.pageOrientation,
content: [{
image: data,
fit: [1000, 1100]
//width: 500,
//height:1100
}],
styles: {
header: {
fontSize: 14,
bold: true,
alignment: 'center',
margin: [0, 10, 0, 10]
}
}
};
pdfMake.createPdf(docDefinition).download(fileName + ".pdf");
},
width: useWidth,
height: useHeight
});
};
You should try implementing a page break:
var docDefinition = {
header: { text: heading, style: 'header' },
footer: {
columns: [
{ text: 'Copyright © 2015 H&R Block. All Rights Reserved.', alignment: 'center',fontSize: 11 }
]
},
pageOrientation: self.pageOrientation,
content: [{
image: data,
fit: [1000, 1100],
pageBreak: 'after', //PAGE BREAK
//width: 500,
//height:1100
}],
styles: {
header: {
fontSize: 14,
bold: true,
alignment: 'center',
margin: [0, 10, 0, 10]
}
}
};

How can align only one column in pdfmake export of datatables.js?

I have managed to edit the pdf export but i need to align only one column
i searched in thes datatables forum and in the pdfmake documentation but i haven't find how to do it. Please help!
My customize function is the following
customize: function ( doc ) {
doc['footer']=(function(page, pages) {
return {
columns: [
'*',
{
alignment: 'right',
text: [
{ text: page.toString(), italics: true },
' de ',
{ text: pages.toString(), italics: true }
]
}
],
margin: [30, 0]
}
});
var d = new Date();
var fecha = d.toLocaleDateString('es-CL');
var hora = d.toLocaleTimeString('es-CL');
doc.content.splice( 1, 0, {
columns: [
{
alignment: 'left',
text: 'Comercial del Real \n Ramón Freire 471, Rancagua \n\n',
bold: true,
},
{
alignment: 'right',
text: 'Fecha: '+fecha+'\nHora: '+hora+' ',
margin: [ 0, 0, 40, 0 ],
bold: true
}
]
});
}
I found a solution on my own
var rowCount = document.getElementById("tableID").rows.length;
for (i = 0; i < rowCount+1; i++) {
doc.content[1].table.body[i][5].alignment = 'right';
};
I really don't know if it is the best solution but it works for me

running a "background task" in javascript

Is it possible, in Javascript, to run a function in background ?
I am generating a pdf with pdfmake tool in an angularJS app, but the pdf generation is quite long (3-4 seconds) and during this time, the ui freeze completely.
I would like to run a background task and force the pdf download without freezing the user ui, is it possible ?
Here how I am running pdfmake (pdfmake and _ are custom factories):
'use strict';
angular.module('App')
.service('CatalogPdfService', ['pdfmake', '_', '$q', '$filter',
function (pdfmake, _, $q, $filter) {
var $translate = $filter('translate');
var listDate = new Date();
return {
download: download
};
function download(data) {
listDate = _.first(data).publishedOn;
console.log('start download');
var deferred = $q.defer();
var filename = $translate('APP.EXPORT.pdf.catalog.title', {date: $filter('amDateFormat')(listDate, 'DDMMYYYY')}) + '.pdf';
create(data).download(filename, function () {
console.log('end download');
deferred.resolve();
});
return deferred.promise;
}
function create(data) {
// group data by category
var dataByCategory = _.groupBy(data, function (d) {
return d.category;
});
// group categories data by subcategory
_.forEach(dataByCategory, function (d, i) {
dataByCategory[i] = _.groupBy(d, function (d) {
return d.subcategory;
});
});
var content = {
table: {
headerRows: 1,
widths: ['*', 20, 10, 20, 20, 20, 20, 40, 20, 30],
body: [
[
{text: $translate('APP.EXPORT.pdf.catalog.header.article') , style: 'headings', alignment: 'left'},
{text: $translate('APP.EXPORT.pdf.catalog.header.mine') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.rank') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.origin') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.transporter') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.culture') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.label') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.unit') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.packing') , style: 'headings'},
{text: $translate('APP.EXPORT.pdf.catalog.header.price') , style: 'headings'}
]
]
},
layout: {
hLineWidth: function (i) {
return (i == 0) ? 0 : 1;
},
vLineWidth: function (i) {
return 0;
},
hLineColor: function (i, node) {
return '#ccc';
}
}
};
_.forEach(dataByCategory, function (data, category) {
content.table.body = content.table.body.concat(renderCategory(category, data));
});
var dd = {};
dd.content = renderHeader().concat(content);
dd.header = function (currentPage, pageCount) {
return {
text: $translate('APP.EXPORT.pdf.catalog.pagecount', {start: currentPage.toString(), end: pageCount.toString()}),
alignment: 'right',
color: '#666',
margin: [0, 20, 40, 0]
};
};
dd.styles = {
title: {
fontSize: 15,
bold: true
},
headings: {
italics: true,
alignment: 'center'
},
flag: {
alignment: 'center',
italics: true,
color: '#666'
},
category: {
bold: true,
fontSize: 12,
margin: [0, 10, 0, 0] // Left, Top, Right, Bottom
},
subcategory: {
bold: true,
fontSize: 10,
margin: [0, 7, 0, 5] // Left, Top, Right, Bottom
}
};
dd.defaultStyle = {
fontSize: 8
};
return pdfmake.createPdf(dd);
}
function renderHeader() {
return [
{image: logo(), height:40, width: 86},
{
margin: [0, 10, 0, 20],
table: {
widths: [100, 100, 100, '*'],
body: [
[
{text: $translate('APP.COMMON.address', {char: '\n'})},
{text: '\n' + $translate('APP.COMMON.phone')},
{text: '\n' + $translate('APP.COMMON.fax')},
{text: '\n' + $translate('APP.EXPORT.pdf.catalog.listno', {date: $filter('amDateFormat')(listDate, 'DD/MM/YYYY')}) , alignment: 'right'}
]
]
},
layout: {
hLineWidth: function (i) {
return (i == 0) ? 0 : 1;
},
vLineWidth: function (i) {
return 0;
}
}
}];
}
function renderCategory(name, data) {
var category = [
[
{text: name, style: 'category', colspan: 10},
'', '', '', '', '', '', '', '', ''
]
];
_.forEach(data, function (data, name) {
category = category.concat(renderSubcategory(name, data));
});
return category;
}
function renderSubcategory(name, data) {
var subcategory = [
[
{text: name, style: 'subcategory', colspan: 10},
'', '', '', '', '', '', '', '', ''
]
];
_.forEach(data, function (product) {
subcategory.push(renderProduct(product));
});
return subcategory;
}
function renderProduct(product) {
return [
product.name,
{
text: (product.isInPrivateList ? 'Oui' : ''),
style: 'flag'
},
{
text: (null === product.rank ? '' : String(product.rank)),
style: 'flag'
},
{
text: (product.origin || ''),
style: 'flag'
},
{
text: (product.transporter || ''),
style: 'flag'
},
{
text: (product.label || ''),
style: 'flag'
},
{
text: (product.culture || ''),
style: 'flag'
},
{
text: product.unit,
margin: [0, 0, 5, 0],
italics: true,
alignment: 'right'
},
{
text: (product.quantity || '1'),
italics: true,
fillColor: '#eee',
alignment: 'center'
},
{
text: product.unitPrice,
margin: [0, 0, 5, 0],
italics: true,
fillColor: '#eee',
alignment: 'right'
}
];
}
function logo() {
return 'data:image/jpeg;base64,blabla bigbase64 string'
}
}]);
You could use a Web Worker to generate the PDF. But you should be aware of some restrictions when using them. Here is a good reference.
I created a factory in Angular for doing work on a worker thread. Something like this:
/*
Here's an example on how to get this sack of moldering spuds to do something:
var myWorker = new MyWorker({ fn: function() {
this.onmessage = function(args) {
setTimeout(function() {
this.postMessage('Got args: ' + args.data);
}, 20000);
};
} });
myWorker.do('Test').then(function(message) {
alert(message);
});
*/
'use strict';
angular.module('myApp')
.factory('MyWorker', function($q) {
var _worker;
var MyWorker = function(settings) {
_init(settings);
};
MyWorker.prototype.do = function(args) {
var deferred = $q.defer();
_worker.onmessage = function(message) {
deferred.resolve(message.data);
};
//Fire up the blades.
if (args)
_worker.postMessage(args);
else
_worker.postMessage();
return deferred.promise;
};
MyWorker.prototype.destroy = function() {
_worker.terminate();
};
function _init(settings) {
if (settings.script)
_worker = new Worker(settings.script);
//Need to make this IE (10+) friendly.
else if (settings.fn) {
var blobUrl = window.URL.createObjectURL(new Blob(
['(', settings.fn.toString(), ')()'],
{ type: 'application/javascript' }
));
_worker = new Worker(blobUrl);
}
};
return MyWorker;
});
This will give you a rough idea about how it can be implemented in AngularJS, but seriously take it with a grain of salt.

apply new theme without reloading the charts in highcharts

Can I apply theme without reloading the whole chart. Can I push the themes settings within the chart code? In highcharts site all examples are single theme based. Here is my code
$(function() {
$.getJSON('http://api-sandbox.oanda.com/v1/candles?instrument=EUR_USD&candleFormat=midpoint&granularity=W', function(data) {
// create the chart
var onadata =[];
var yData=[];
var type='line';
var datalen=data.candles.length;
var all_points= [];
var all_str="";
for(var i=0; i<datalen;i++)
{
var each=[Date._parse(data.candles[i].time), data.candles[i].openMid, data.candles[i].highMid, data.candles[i].lowMid, data.candles[i].closeMid]
onadata.push(each);
yData.push(data.candles[i].closeMid);
}
$( "#change_theme" ).on("change", function() {
var optionSelected = $("option:selected", this);
var valueSelected = this.value;
//alert(valueSelected);
if(valueSelected=='default.js')
{
location.reload();
}
else{ $.getScript('js/themes/'+valueSelected, function() {
//alert('Load was performed.');
chart();
});
}
});
chart();
function chart()
{
$('#container').highcharts('StockChart', {
credits: {
enabled : 0
},
rangeSelector : {
buttons: [{
type: 'month',
count: 1,
text: '1M'
}, {
type: 'month',
count: 3,
text: '3M'
},{
type: 'month',
count: 6,
text: '6M'
},{
type: 'all',
text: 'All'
}],
selected:3
},
legend: {
enabled: true,
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
borderWidth: 0
},
title : {
text : 'Stock Price'
},
xAxis :{
minRange : 3600000
},
yAxis : [{
offset: 0,
ordinal: false,
height:280,
labels: {
format: '{value:.5f}'
}
}],
chart: {
events: {
click: function(event) {
var x1=event.xAxis[0].value;
var x2 =this.xAxis[0].toPixels(x1);
var y1=event.yAxis[0].value;
var y2 =this.yAxis[0].toPixels(y1);
selected_point='['+x1+','+y1+']';
all_points.push(selected_point);
all_str=all_points.toString();
if(all_points.length>1)
{
this.addSeries({
type : 'line',
name : 'Trendline',
id: 'trend',
data: JSON.parse("[" + all_str + "]"),
color:'#'+(Math.random()*0xEEEEEE<<0).toString(16),
marker:{enabled:true}
});
}
if(all_points.length==2)
{
all_points=[];
}
}
}
},
series : [{
//allowPointSelect : true,
type : type,
name : 'Stock Price',
id: 'primary',
data : onadata,
tooltip: {
valueDecimals: 5,
crosshairs: true,
shared: true
},
dataGrouping : {
units : [
[
'hour',
[1, 2, 3, 4, 6, 8, 12]
], [
'day',
[1]
], [
'week',
[1]
], [
'month',
[1, 3, 6]
], [
'year',
[1]
]
]
}
},
]
});
}
});
});
and this is my js fiddle
Please help. Thanks in advance.
This is possible if you're using modern browsers that support CSS variables.
Highcharts.theme = {
colors: [
'var(--color1)',
'var(--color2)',
'var(--color3)',
'var(--color4)',
'var(--color5)',
'var(--color6)',
]
}
Highcharts.setOptions(Highcharts.theme);
function setTheme(themeName) {
// remove theme-* classes from body
removeClasses = Array.from(document.body.classList).filter(s => s.startsWith('theme-'));
document.body.classList.remove(...removeClasses)
if (themeName) {
document.body.classList.add('theme-' + themeName);
}
}
CSS
body {
--color1: #e00;
--color2: #b00;
--color3: #900;
--color4: #600;
--color5: #300;
--color6: #000;
}
body.theme-dark {
--color1: #555;
--color2: #444;
--color3: #333;
--color4: #222;
--color5: #111;
--color6: #000;
}
body.theme-retro {
--color1: #0f0;
--color2: #ff0;
--color3: #0ff;
--color4: #0a0;
--color5: #aa0;
--color6: #00a;
}
Unfortunately it is not possible, so you need to destroy and create new chart.

Categories

Resources