Build table dynamically with PDFmake - javascript

I found an example of how to fill the table with pdfmake dynamically, which has 2 columns. Now I tried add another column 'height' to the table but I don't know how to modify it.
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)
}
};
}
function Pdftest(){
var externalDataRetrievedFromServer = [
{ name: 'Bartek', age: 34, height: 1.78 },
{ name: 'John', age: 27, height: 1.79 },
{ name: 'Elizabeth', age: 30, height: 1.80 },
];
var dd = {
content: [
{ text: 'Dynamic parts', style: 'header' },
table(externalDataRetrievedFromServer, ['name', 'age', 'height'])
]
}
pdfMake.createPdf(dd).download();
}
Does anybody know what needs to be modified?

Error was anywhere else, the code above works perfectly fine

Related

How can I remove, hide one checkbox when using formatter:"rowSelection" in Tabulator.js?

I am using
{formatter:"rowSelection", titleFormatter:"rowSelection", hozAlign:"center", headerSort:false, cellClick:function(e, cell){
table.recalc();
}},
but I do not want the marked checkbox (see the picture) to be displayed. You can use jsFiddle to try out.
Is that possible using Tabulator functionality? If not then I am thinking that I can remove it from DOM in renderComplete function.
UPDATE1
actually I do not want all checkboxes that are on the "parent" level.
You can create custom formatter for it
Here i have created custom DOM element and only returned from formatter function if some conditions are met otherwise returned null which cause it to render empty cell.
Tabulator use selectRow module for selection
In custom formatter i checked if user has enabled selectable option if yes then it will enable selectRow module, then tested if its row or table if its a row then checkbox will select/deselect row which i registerd in tabulator to let it know that use this checkbox component, if it is not a row then it would be table for that i registered checkbox to header selection which selects/deselects entire table.
var do_not_show_checkbox_ids = [1];
var tableDataNested = [{
id: 1,
name: "BalanceOil",
_children: [{
id: 11,
name: "BalanceOil+",
cena: 31,
mn: 1,
cena_1: 159
},
{
id: 12,
name: "BalanceOil Aqua",
cena: 41,
mn: 1,
cena_1: 159
},
]
},
{
id: 2,
name: "Xtend",
cena: 23,
mn: 1
},
{
id: 2,
name: "Xtend",
cena: 23,
mn: 1
},
{
id: 2,
name: "Xtend",
cena: 23,
mn: 1
},
{
id: 3,
name: "Zinobiotic",
cena: 24,
mn: 1
}
];
var table = new Tabulator("#example-table", {
movableColumns: true,
data: tableDataNested,
dataTree: true,
selectable: true,
columns: [{
title: "Name",
field: "name",
headerSort: false,
width: 200
},
{
title: "Cena",
field: "cena",
headerSort: false
},
{
formatter: function(cell, formatterParams, onRendered) {
const data = cell.getRow().getData();
if (do_not_show_checkbox_ids.indexOf(data['id']) == -1) {
var checkbox = document.createElement("input");
checkbox.type = 'checkbox';
if (this.table.modExists("selectRow", true)) {
checkbox.addEventListener("click", (e) => {
e.stopPropagation();
});
if (typeof cell.getRow == 'function') {
var row = cell.getRow();
if (row._getSelf().type == "row") {
checkbox.addEventListener("change", (e) => {
row.toggleSelect();
});
checkbox.checked = row.isSelected && row.isSelected();
this.table.modules.selectRow.registerRowSelectCheckbox(row, checkbox);
} else {
checkbox = "";
}
} else {
checkbox.addEventListener("change", (e) => {
if (this.table.modules.selectRow.selectedRows.length) {
this.table.deselectRow();
} else {
this.table.selectRow(formatterParams.rowRange);
}
});
this.table.modules.selectRow.registerHeaderSelectCheckbox(checkbox);
}
}
return checkbox;
}
return null;
},
titleFormatter: "rowSelection",
hozAlign: "center",
headerSort: false,
cellClick: function(e, cell) {
this.recalc();
}
},
{
title: "mn",
field: "mn",
editor: "number",
headerSort: false,
cellEdited: function(cell) {
updateSum(cell);
}
},
{
title: "Sum",
field: "sum",
headerSort: false
}
],
rowClick: function(e, row) {
// console.log(table.getRows().length);
},
renderComplete: function(t) {
this.getRows().forEach(function(value, index) {
console.log(value.isSelected());
var children = value.getTreeChildren();
for (let j = 0; j < children.length; j++) {
const name = children[j].getData().name;
}
children.forEach(function(value, index) {
// console.log("cena");
var cena = value.getData().cena; //price
// console.log(cena);
var mnozstvi = value.getData().mn; //amount
value.update({
sum: cena * mnozstvi
});
});
updateSum(value.getCell("mn"));
});
},
selectableCheck: function(row) {
//row - row component
return row.getData().cena > 0; //allow selection of rows where the age is greater than 18
},
});
function updateSum(cell) {
var cena = cell.getData().cena; //price
var mnozstvi = cell.getValue(); //amount
if (mnozstvi) {
cell.getRow().update({
sum: cena * mnozstvi
});
}
}
Here working example
tabulator documentation links - custom formatter
Note: For info about how tabulator formatters works internally check here

Kendo Grid can't reload template again after remove row data?

When I remove the second line of data, a field on the first line appears undefined, that is, unable to execute the template:
{
field: "professionLevel",
title: '<#spring.message "技能等级"/>',
width: 120,
template: function (dataItem) { //meaning of the template: Just convert English to Chinese,
for (var i = 0; i < professionLevelData.length; i++) {
if (professionLevelData[i].value === dataItem.professionLevel) {
return dataItem.professionLevel = professionLevelData[i].meaning;
}
}
}
}
I think you're doing something else wrong. With the code you provided I cant salvage enough to reproduce the problem so I will present you with a simple working case (similar to your template logic):
var data = [
{id: 2, name: "John Doe", age: 33},
{id: 1, name: "Jane Doe", age: 30},
{id: 3, name: "Bob Doe", age: 22}
];
var translate = [
{value: 1, translate: "traslate 1"},
{value: 2, translate: "traslate 2"},
{value: 3, translate: "traslate 3"}
];
$("#grid").kendoGrid({
columns: [
{
field: "name",
template: function (dataItem) {
var returnValue = dataItem.name;
for (var i = 0; i < translate.length; i++) {
if (dataItem.id === translate[i].value) {
returnValue = translate[i].translate;
}
}
return returnValue;
}
},
{field: "age"},
{command: "destroy"}
],
dataSource: {
data: data,
schema: {
model: {id: "id"}
}
},
editable: true,
remove: function (e) {
console.log("Removing", e.model.name);
}
});
Example: Translate example
If this doesn't solve your problem please expand the question with relevant data.
PS. With return dataItem.professionLevel = professionLevelData[i].meaning; you are updating grids data source, are you sure you want to update it? Or just show meaning in table? In that case just return professionLevelData[i].meaning;

Add html elements with Javascript using loop

Through an object, I need to create HTML elements using the Javascript loop.
The sample code displays the contract, contract lots and batch invoices.
Well, I just need to display the contracts as a list and, for each contract, add the lots of the contract in a drop-down list (selection option)` and then display the invoices in another list, according to the selected lot.
there was a problem displaying batches, but Barmar's comment helped me
Resume:
1: Create an event to load invoices when changing the batch
2: load the lots and invoices related to the contract by clicking on the contract.
Sample code
UPDATE: Added loading invoices
<div id="div-contracts"></div>
<ul id="div-invoices"></ul>
<style>
#div-contracts {
display: table-cell;
width: 100%;
}
.loren {
float:left;
width: 45px;
}
.ipsulum {
float:left;
width: 35%;
}
</style>
<script>
let object = {
name: 'wagner',
contracts: [
{
id: 1,
contract: '123',
batches: [
{
id: 1,
batch: '1',
invoices: [
{
value: 10,
due: '01/01/2020',
},
{
value: 10,
due: '01/02/2020',
},
],
},
{
id: 2,
batch: '2',
invoices: [
{
value: 10,
due: '01/04/2020',
},
{
value: 10,
due: '01/05/2020',
},
],
},
]
},
{
id: 1,
contract: '456',
batches: [
{
id: 3,
batch: '1',
invoices: [
{
value: 15,
due: '01/01/2020',
},
{
value: 14,
due: '01/02/2020',
},
],
},
]
},
]
};
let getById = (id, parent) => parent ? parent.getElementById(id) : getById(id, document);
const DOM = {
contract: getById('div-contracts'),
};
function htmlForBatchsOptions(contract) {
return contract.batches.map(batch => `<option value="${batch.id}">${batch.batch}</option>`).join('');
};
function htmlForInvoices(invoices) {
let invoiceLi = '';
for (const invoice of invoices) {
invoiceLi += `<li>${invoice.value} - ${invoice.due}</li>`;
}
return document.querySelector('#div-invoices').innerHTML = invoiceLi;
};
for (const contract of object.contracts) {
DOM.contract.innerHTML +=
`<div class="contract">
<div class="loren">
<span>${contract.contract}</span>
</div>
<div class="ipsulum">
<select class="sel-batch">
${htmlForBatchsOptions(contract)}
</select>
</div>
</div>`;
let = contractInAttendance = 1;
let batchFilteredByContractId = contract.batches.filter(batch => batch.id === contractInAttendance);
htmlForInvoices(batchFilteredByContractId[0].invoices);
}
</script>

Extracting values from array-attributes of a JSON array

I have an array of elements and each element is super-complex because its attributes are arrays which contains other arrays as properties. I want to extract just few attributes of this element, I've tried with the forEach function but it doesn't work.
The array comes from a json file, that's why I use axios, and the elements of the array are something like this:
{
"ITEMS":[
{
"id":"0001",
"name":"foo",
"sizes":[
{
"name":"small",
"id":"9999",
"available":"no"
},
{
"name":"medium",
"id":"9998",
"available":"yes"
}
]
},
{
"id":"0002",
"name":"bar",
"sizes":[
{
"name":"small",
"id":"4444",
"available":"yes"
},
{
"name":"medium",
"id":"4443",
"available":"no"
}
]
},
...
]
}
So I want to collect the their attributes creating elements that are PUSHED in an array and that replicate this model:
this.sample = {
colour:'item.name',
size:'item.size.name[i]',
idcode:'item.id',
sizecode:'item.size.id[i]',
available:'item.size.available[i]'
}
this is my attempt (not working)
const axios = require('axios');
class IndianaJones {
constructor(){
this.sample = {
name:'',
colour:'',
size:'',
idcode:'',
sizecode:'',
available:''
},
this.newids = ["10","11","12"...]
this.freshprods = []
}
async FreshProd(){
for(this.i=0;this.i<this.newids.length;this.i++){
this.prod = await axios.get(`https://blablabla/${this.newids[this.i]}.json`)
this.ITEMS.forEach(function(item){
this.sample.idcode=item.id;
this.sample.colour=item.name;
item.sizes.forEach(function(SIZE){
this.sample.size=SIZE.name
this.sample.sizecode=SIZE.id
this.sample.available=SIZE.available
this.freshprods.push(this.sample)
})
}
)
}
return this.freshprods
}
}
(async()=>{
const indiana = new IndianaJones();
await indiana.FreshProd()
})()
Really, this is driving me up to wall, i would be SO GRATEFUL for anyone who can help me, maybe LODASH could be useful?
You are trying to flatten the structure. To so you can use Array.flatMap() (or lodash's _.flatMap() to iterate the ITEMS, map the sizes array, and return a new object for each size:
const prods = {"ITEMS":[{"id":"0001","name":"foo","sizes":[{"name":"small","id":"9999","available":"no"},{"name":"medium","id":"9998","available":"yes"}]},{"id":"0002","name":"bar","sizes":[{"name":"small","id":"4444","available":"yes"},{"name":"medium","id":"4443","available":"no"}]}]};
const freshprods = prods.ITEMS.flatMap(
({ id: idcode, name: colour, sizes }) =>
sizes.map(o => ({
colour,
size: o.name,
idcode,
sizecode: o.id,
available: o.available
}))
);
console.log(freshprods);
let prod = {
"ITEMS":[
{
"id":"0001",
"name":"foo",
"sizes":[
{
"name":"small",
"id":"9999",
"available":"no"
},
{
"name":"medium",
"id":"9998",
"available":"yes"
}
]
},
{
"id":"0002",
"name":"bar",
"sizes":[
{
"name":"small",
"id":"4444",
"available":"yes"
},
{
"name":"medium",
"id":"4443",
"available":"no"
}
]
}
]
}
let freshprods = [];
prod.ITEMS.forEach(function(item){
item.sizes.forEach(function(SIZE){
freshprods.push({
idcode: item.id,
colour: item.name,
size: SIZE.name,
sizecode: SIZE.id,
available: SIZE.available
})
})
})
console.log(freshprods);
Output
[ { idcode: '0001',
colour: 'foo',
size: 'small',
sizecode: '9999',
available: 'no' },
{ idcode: '0001',
colour: 'foo',
size: 'medium',
sizecode: '9998',
available: 'yes' },
{ idcode: '0002',
colour: 'bar',
size: 'small',
sizecode: '4444',
available: 'yes' },
{ idcode: '0002',
colour: 'bar',
size: 'medium',
sizecode: '4443',
available: 'no' } ]

Uncaught TypeError in jsPdf autotable

Am trying to print the dynamic data's into pdf file using jsPdf Auto-table . When am doing this i got some kind of error's like
The headers should be an object or array, is: function (jspdf.plugin.autotable.js:10 )
The data should be an object or array, is: function (jspdf.plugin.autotable.js:10)
TypeError: t.forEach is not a function (angular.js:12314)
Here is my code -
var getColumns = function () {
return [
{ title: "ID", dataKey: "id" },
{ title: "Name", dataKey: "first_name" },
{ title: "Email", dataKey: "email" },
{ title: "City", dataKey: "city" },
{ title: "Country", dataKey: "country" },
{ title: "Expenses", dataKey: "expenses" }
];
};
function getData(mainSteps) {
mainSteps = mainSteps || 4;
//var sentence = faker.lorem.words(12);
var data = [];
for (var j = 1; j <= rowCount; j++) {
data.push({
id: j,
first_name: this.getSteps(),
email: this.getSteps(),
country: this.getSteps(),
city: this.getSteps()(),
expenses: this.getSteps()
// text: shuffleSentence(sentence),
//text2: shuffleSentence(sentence)
});
}
return data;
}
var pdfsize = 'a4';
var doc = new jsPDF('p', 'pt', pdfsize);
doc.autoTable(getColumns, getData, {
theme: 'grid', // 'striped', 'grid' or 'plain'
headerStyles: {
fillColor: [12, 234, 227],
textColor: [12, 1, 1]
},
// margin: { top: 50, left: 20, right: 20, bottom: 0 },
styles: {
overflow: 'linebreak',
columnWidth: 88
},
beforePageContent: function (data) {
doc.text("Process Name :" + mainData.name + " || " + "Description :" + mainData.description, 40, 30);
},
//startY: doc.autoTableEndPosY() + 20,
columnStyles: {
0: { columnWidth: 200 }
}
});
//startY: doc.autoTableEndPosY() + 20
doc.save(mainData.name + ".pdf");
Note : If the error is in my code mean's i can find the solution for that, But it says error is in (jspdf.plugin.autotable.js:10 ) and (angular.js:12314) so am getting confused here. Can someone clarify me please.
As the errors explain the input data must be arrays or objects. In your case that simply means calling the functions instead of sending them to autotable. Replace this
doc.autoTable(getColumns, getData, {...});
with
doc.autoTable(getColumns(), getData(), {...});

Categories