How to avoid table data splitting between two pages using jspdf - reactjs

Is there a way to fix data splitting between two pages (I've posted the image of the issue below). I'm passing a template to doc.html() and im using jspdf to generate a pdf file. Everything is displayed as it should except the table data - the table data splits between two pages (see image below).
This is the part of the code to generate the pdf. HTMLTemplate is html template im passing to it (divs and tables with data)
let doc = new jsPDF("p", "pt", "a4");
doc.html(renderToString(<HTMLTemplate />), {
margin: [20, 0, 20, 0]
async callback(doc) {
// save the document as a PDF
doc.save(name_pdf_file, { returnPromise: true }).then(SetValue()); // Ignore this
},
});

Related

How do I make a query to get multiple content type in spfx? #pnp

I am making a project with React and Typescript (SPFX) and I have created a webpart with about 4 components and each component has a different Content type id. At the moment, I need to make a query search every time I want to get the CT information in each component, like this:
const searchNews = async () => {
const searchQuery: ISearchQuery = {
Querytext: [
'ContentTypeId:0x010100C568DBD9B2FDCC96666E9F2007948130EC3DB054237AF39005CF914A33608B74B8D05F01*',
'ANFHighlightOWSBOOL=1'
].join(' '),
RowLimit: 10,
SelectProperties: [
'Title',
'Path',
'ANFHighlightOWSBOOL',
'ANFImageOWSIMGE',
'AuthorOWSUSER',
'CreatedOWSDate'
]
};
They have mostly all the same SelectProperties, with specific differences.
I saw we can do a query in the webpart property pane to have a CT dynamic return so we can access it in the component. I also saw this tutorial: https://learn.microsoft.com/en-us/sharepoint/dev/spfx/dynamic-data But I am not quite sure how to make it and if there is a way to do it without de dynamic import.
How can I do a query in the property pane and make it usable in all my possible components?

How to allow user to paste spreadsheet data (multiple columns) into a textarea

Im working on a system which should allow the user to copy data from a spreadsheet and paste into an inputfield(text area). Each row received should then be handled as an object with x properties within.
For example
{
emailaddress: xxx#xxx.com,
firstname: Siri,
lastname: Hemsley,
address: 154 Example Dr, Hendersonville NC
}
I have seen that Mailchimp is using something very similar but not sure how it's done.
I'm mostly using react and various npms (still a beginner)
Any suggestions or example of code that can do this?
Image of mailchimp below:
If you copy content from Google Spreadsheets (I am not sure about Excel etc.) an output comes with tabs \t between each value and new lines \n between each row.
You can split textarea value by tabs and new lines and transform it to an object. Something like this:
const textArea = document.querySelector('textarea');
textArea.addEventListener('change', (e) => {
const lines = e.target.value.split('\n')
lines?.forEach(line => {
console.log("this is one line", line);
const values = line.split('\t');
console.log("values of line", values);
});
});
<textarea></textarea>
I hope it helps you somehow.

JsPDF Inaccurately Displays Tables

I'm attempting to use JsPDF and HTML2Canvas to create a downloadable PDF file. I'm using AngularJs. The HTML content contains several long tables that are sometimes 3-4x the width of the PDF. Although the PDF ends up close to perfect, I've run into several defects:
Table columns occasionally don't display their respective values in the PDF (partial or no data)
If a date field is not provided, the table in the PDF defaults to 12/31/9999
A single row table shifts all its values left by one. As a result, there is no record/ID and the last column value is undefined.
I tried messing around with margins and content width. Unfortunately, only margin.top/left have any effect.
This is the code I've used:
$scope.savePdfSample = function(value) {
html2canvas(document.body,{
onrendered:function(canvas){
var doc = new jsPDF('l', 'pt', 'letter'),
source = $("#template_invoice")[0], //not sure what this does
margins = {
top: 40,
bottom: 40,
left: 40,
right:40,
width: 1000,
};
doc.fromHTML(
$("#ccsReport1").html(),
margins.left,
margins.top,
{
'width': margins.width,
},
function(dispose) {
doc.save('CCS Case Details.pdf');
},
margins
);
}
});
};
I've tried messing around with the jspdf.debug.js file, but I haven't found a solution yet. I'd appreciate anyone pointing me in the right direction. Thank you.

Scala Play Framework image upload with Angular ng-file-upload

I am using Angular ng-file-upload (https://github.com/danialfarid/ng-file-upload) on the frontend to manage the file upload process.
Unfortunately, form contains a complex object with multiple files. Using the MultipartFormData (https://www.playframework.com/documentation/2.5.x/ScalaBodyParsers) on the server side I have successfully decomposed the uploaded content and can read it from the request.body.
Now, to my surprise, I do not have a simple Json Objects but rather a strangely formed datatype, described on the ng-file-upload website as:
(...) server implementations expecting nested data object keys in .key or [key] format.
Example: data: {rec: {name: 'N', pic: file}} sent as: rec[name] -> N, rec[pic] -> file
data: {rec: {name: 'N', pic: file}, objectKey: '.k'} sent as: rec.name -> N, rec.pic -> file
So far I have managed to bring all the data to a common MultipartFormData.Part type, using the DataPart and FilePart like this:
val opts = body.dataParts.map {
case (key, values) => DataPart(key, values.head)
}
val parts = opts ++ body.files
So I am now left with a quite unfortunate Iterable[Part]:
0 = {MultipartFormData$DataPart#86271} "DataPart(arabic[active],false)"
1 = {MultipartFormData$DataPart#86273} "DataPart(english[active],true)"
2 = {MultipartFormData$DataPart#86277} "DataPart(english[url],2132132132)"
...
7 = {MultipartFormData$FilePart#76473} "FilePart(english[image],fb_icon_325x325.png,Some(image/png),TemporaryFile(/tmp/playtemp5909927824995768544/multipartBody8348573128070542611asTemporaryFile))"
Each object name contains the key of it's Json structure and its according value. Now instead of key[level1][level2] I would like to parse it to objects, in my case:
case class PcBanner(english: PcBanners, arabic: PcBanners, kurdish: PcBanners)
case class PcBanners(active: Boolean, url: Option[String], image: Option[String])`
I hope you got the idea.
The question
I know I could try to parse the name strings trying to fit it to objects, but I believe I made a mistake someway in the middle.
Is there a way to parse this structure into the objects, using field names as a reference? Any build in Play functions or alike?
Thanks for help!
As I stated in the title my case was to send images. As you would expect, I am also presenting a preview and the files currently saved in the database.
Considering all pros and cons I have decided to send all the data in JSON format, both ways. Meaning that the images are encoded and sent along in JSON structure.
Despite the fact that above solution looks very convenient it actually creates new problems during the implementation.
You will quickly exceed the server's POST request size limit. For Play server the default 100kB is possible to be extended, but...
I have soon run into some data malformations as the image saved as huge String of bytes probably had some sending/parsing errors.
Not going deeper into this faulty solution I have used the #danial advice:
No have the file sent separately like this
{file: file, otherData: JSON.stringify(myData)}
My solution
If anyone would like to use similar approach to mine I present my answer.
On the front-end side I have decided used ng-file-upload library. Binding it to HTML component with ngf-select with ngf-drop which enables the component:
<div ngf-drop ngf-select
ng-model="image"
ngf-accept="'image/*'"
ngf-resize="{width: {{width}}, height: {{height}}, quality: 1.0, restoreExif: false}">
<img ng-show="!!image && !!image.$ngfName" ngf-src="image">
<img ng-show="(!image || !image.$ngfName)" ng-src="{{ imageUrl }}">
</div>
Inside the upload tag I put the image preview. This works flawlessly. If the image is not selected I use the image saved in the db.
The data and images do not share the model anymore. The upload function looks as follow:
return Upload.upload({
url: url,
data: {file: images, data: angular.toJson(data)}
}).then(function (resp) {
console.log(resp);
}, function (error) {
console.log(error);
});
Putting together all the above gave me the output data object:
{
"english":{
"active":true,
"url":"http://google.com"
},
"arabic":{
"active":true,
"url":"http://google.com"
},
"kurdish":{
"active":true,
"url":"http://google.com"
}
}
On the server side the JSON matches the prepared case class and is parsed with build-in Jackson parser, allowing for easy object manipulation. The image has to be manually selected:
val json = r.body.dataParts("data")
val jsValue = Json.parse(json.head)
val result = jsValue.validate(LocalizedBanner.dataModelFormat) // parse JSON
Extracting the files from body can be done with build in function .file:
val key = s"file[${lang.name}][${imageType.name}]"
body.file(key).map(mp => (mp.ref.file, imageType))
Enjoy!

How to provide tooltips for multiple line charts

I'm converting existing code that displays a single line graph to display multiple line graphs at once. I would like each line graph to have tooltips. My code looks like this:
var line = new RGraph.Line('canvas', data).Set('tooltips', tips)
I can make multiple line graphs render by changing the data array from 1-dimension to 2-dimensions.
But I have also converted the tips array to 2-dimensions, and no tooltips are appearing at all.
Am I correct in assuming that the tips array is assumed to be 1-dimensional, and if so how do I specify tooltips for multiple line charts?
Give them as one big array. If you have them in multiple arrays you can use the JavaScript concat() function or you just add them together like below.
Here's an example without using the concat function:
new RGraph.Line({
id: 'cvs',
data: [
[8,4,3],
[9,8,1]
],
options: {
tooltips: ['A', 'B','C','D','E','F']
}
}).draw();
And here's an example using concat():
tooltips_a = ['A', 'B','C'];
tooltips_b = ['D','E','F'];
tooltips_combo = tooltips_a.concat(tooltips_b);
new RGraph.Line({
id: 'cvs',
data: [
[8,4,3],
[9,8,1]
],
options: {
tooltips: tooltips_combo
}
}).draw();

Resources