PhantomJs - How to render a multi page PDF - node.js

I can create one-page PDFs with phantomJS; but I can't find on the doc how to create different pages (each page coming from an html view) and put them into one PDF ? I am using node-phantom module for NodeJS

Just need to specify a paperSize.
Like this with module "phantom": "0.5.1"
function(next) {
phantom.create(function(doc) {
next(null, doc);
}, "phantomjs", Math.floor(Math.random()*(65535-49152+1)+49152));
},
function(ph, next) {
ph.createPage(function(doc) {
next(null, doc);
});
},
function(page, next) {
page.set('paperSize', {format: 'A4', orientation: 'portrait'});
page.set('zoomFactor', 1);
}
Then, simply use page-break-before: always; in your HTML content each time you want to open a new page.
PS: I use async.waterfall in this example
PPS: Math.random on port number is used to avoid module crash if concurrent calls to phantom binary are triggered. Works fine - if someone wants to post something better even if a bit off-topic, feel free to do it

Related

Best way to implement notification alerts in Node/Express app?

I have inherited a Node/Express code base and my task is to implement a notification alert in the navigation menu. The app database has a table of 'pending accounts', and the alert needs to expose the number of these pending accounts. When an account is 'approved' or 'denied', this notification alert needs to update and reflect the new total of pending accounts.
I know how to do the styling and html here, my question is how best to instantiate, maintain and pass a global dynamic variable that reflects the number of pending accounts, and how to get this variable exposed in the header view which contains the navbar where the notification is to be displayed.
This project a pretty standard Node/Express app, however it uses the view engine Pug. At the root of the view hierarchy is a layout.pug file, which loads most of the scripts and stylesheets, and this layout view in turn loads the header Pug view. When this header view loads, and every time it loads, I need this updated 'pending accounts count' value available to insert into the header view. This is what I am at a bit of a loss on how to go about.
Below is the layout.pug markup with the inclusion of the header pug view. Everything else in the project is pretty straightforward vanilla Node/Express I believe, but I am not very experienced with this stack so if any other code is needed please don't hesitate to ask and I will post. Thanks.
doctype html
html(lang="en")
head
meta(charset='utf-8')
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(name='viewport', content='width=device-width, initial-scale=1.0, shrink-to-fit=no')
meta(name='theme-color', content='#4DA5F4')
meta(name='csrf-token', content=_csrf)
script(src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js")
block head
body
include partials/header
I tried including a script in my header.pug view, which contains the navbar element that I want to append the notification too...
link(href='/css/header/header.css' rel='stylesheet')
script(src='/js/registration/registrationsTable.js')
...
Which would run the following function on DOM load....
function getNumberOfPendingRegistrations() {
axios({
method: 'get',
url: '/admin/getNumberOfPendingRegistrations'
}).then((response) => {
if (response.status === 200) {
const { numPendingUsers } = response.data;
console.log(response.data);
} else {
console.log(response.status);
}
})
.catch((error) => {
console.error(error);
});
}
(() => {
document.addEventListener('DOMContentLoaded', () => {
getNumberOfPendingRegistrations();
which would then call the following express function....
exports.getNumberOfPendingRegistrations = (req, res) => {
RegisteredUser.find({ status: 'PENDING' }, (err, allPendingUsers) => {
if (!err) {
let numPendingUsers = 0;
allPendingUsers.forEach(() => {
numPendingUsers++;
});
return res.send(200, { numPendingUsers });
}
console.log('error');
throw err;
});
};
which would then return numPendingUsers to the axios then() function and make that variable available to the header.pug view here....
li.nav-item
a.nav-link(href='/admin/registeredUsers')
span.fa-solid.fa-pen-to-square(style="font-size: 0.7em;")
span(style="margin-left: 0.1em;") Registrations
span.notification=numPendingUsers
NumPendingUsers returns correctly to the axios .then() promise, but is somehow never made available in the header.pug view. It is always undefined. I'm not sure if its a timing issue w when the DOM is loaded, or if I'm making the variable available in .then() incorrecly or what. And also I feel like there must be a simpler way to accomplish all of this.
Figured out that I simply needed to implement a middleware to pass this data to every route. Duh.

why am I getting favicon.ico when i am using findOne method for express params routes?

when i am using list.save() method a object other than customList name which is favicon.ico is also
saving as record in following cod, Why am i gatting favicon.ico as object.
app.get('/:listRoute',function (req,res) {
const customList=(req.params.listRoute);
List.findOne({name:customList }, function (err,result) {
if (!err) {
if (!result) {
const list=new List({
name: customList,
items: defaultItems
})
list.save();
} else {
console.log(result);
res.render('list', {
listTitle: result.name,
latestItems: result.items})
}
}
});
})
When you visit a website (any URL on that website), a browser will typically also send a request to that same domain for /favicon.ico so see if the web site offers an icon to be a visual representation of the site.
Since you are using a wildcarded top level route:
app.get('/:listRoute', ...)
That will get hit by the request for /favicon.ico. Some other urls you also may need to watch out for being requested are: /robots.txt, /humans.txt, /sitemap.xml, /ads.txt.
There are a number of ways to work around this:
Your wildcard route can first check req.url or req.params.listRoute to see if it's something it should ignore.
You can place other top level routes that you want to keep out of your wildcard route in a position before this route so they don't end up in this one.
Don't use a top level wildcard route. Instead, use something like /list/:listRoute so it won't automatically match any top level http request. Your use of a top level wildcarded route interferes with other future uses of your site and can create backwards compatibility going forward when you want to add other top level routes to your site. Imagine if sometime in the future, you want to add /contact or /login or /logout. Those all conflict with /:listRoute.
Try to add a callback function to the list.save();
Let me know if this works. The reason is maybe because of sync issues. eg: time taken by mongoDB to update the first document & save > the time taken by the 'Get' method to redirect to itself. Therefore by adding this callback it kinda make sure the code gets saved first and err checked before the redirect.
eg:
list.save(function(err){
if(!err) {
console.log("list is successfully saved"); //log is optional
res.redirect("/" + listRoute);
}
});
When fetching route data using params with express,the entered data easily we can log.But if not adding top-level route and just trying to get the required route eg:
app.get("/:requireddata",function(req,res){
const data = req.params.requireddata;
});
in this case, when loading main page the favicon.ico will generate as a result.
So for getting an exact result, that's when only loading requireddata route we can get the result by using higher level route.
In case there is no higher-level route add just an additional route before requireddata as shown below:
app.get("/add/:requireddata",function(){});
Here /add/ is an additional route for avoiding favicon.ico
For me this worked, so if this information is useful just go head.
Hey there I also came across this exact issue.
So here is my solution to that.
Just enclose everything in a if block and there you go. DONE !!!!
app.get("/:name", function (req, res) {
if (req.params.name != "favicon.ico") {
const name = _.capitalize(req.params.name);
List.findOne({ name: name }, (err, foundList) => {
if (!err) {
//new list with default items created
if (!foundList) {
const list = new List({
name: name,
items: defaultItems,
});
list.save();
res.redirect("/" + name);
} else {
res.render("list", {
listTitle: foundList.name,
newListItem: foundList.items,
});
}
}
});
}
});
P.s.:- It will throw some error from mongo but that'll not affect the overall working.
Hope this helps.

Why express app.render loads images with local file path synchronously? - NodeJS

I noticed the following:
When I want to render an array of image URLs hosted in another website like this:
index.js
MongoClient.connect('mongodb://localhost:27017/sample', function(err, db) {
db.collection('collSample').findOne( { somevar: 'somevalue' }, function(err, doc) {
assert.equal(null, err);
res.render('index', {
title: 'render images' ,
imagesPaths: doc.images.`**URLs**`
});
db.close();
});
});
index.jade
- each path in imagesPaths
span
img(src="#{path}")
I get this Network info from Chrome Developer Tools. Notice how the images are being rendered asynchronously
But, if I use images stored localy like this:
index.js
MongoClient.connect('mongodb://localhost:27017/sample', function(err, db) {
db.collection('collSample').findOne( { somevar: 'somevalue' }, function(err, doc) {
assert.equal(null, err);
res.render('index', {
title: 'render images' ,
imagesPaths: doc.images.`**localPath**`
});
db.close();
});
});
index.jade
- each path in imagesPaths
span
img(src="#{path}")
I get this Network info. Note that iamges are being rendered synchronously. Also, load time is considerably lower.
Question 1: How can I force rendering images asynchronously?
Question 2: Why sometimes nodejs renders images async and others sync? I thought that nodejs will render a bunch of images asynchronously, regardless of what is inside of image src attribute.
They are not loaded synchronously. All the requests are made async, (see that all the white rectangles begin to be printed at the same time).
The real difference stands in the path of the request towards the resource location:
every remote request get through the Internet and it is not guaranteed that the arrival order is the same at the start.
every local request is processed quite immediately, giving the illusion to be sync, quite preserving the order of the requests too.
Proof: Try add the local path of avery huge image o(N*10MB) or o(100MB) (for example http://www.nasa.gov/sites/default/files/thumbnails/image/hs-2015-02-a-hires_jpg.jpg ) in the middle of the array and see that the image is finishes to be load last.

MEAN stack: Wondering api.js and crud.js

I'm studying MEAN stack these day, So I make some sample apps following guidance. I made up "Bookshelf" application just few hours ago, this is provided by google cloud service, so I should delve into sample code to understand how it works.
Whole source code : https://github.com/GoogleCloudPlatform/nodejs-getting-started/tree/master/2-structured-data
Sample application : http://mymongo-1165.appspot.com/books
books/api.js
router.get('/', function list(req, res) {
model.list(10, req.query.pageToken,
function(err, entities, cursor) {
if (err) { return handleRpcError(err, res); }
res.json({
items: entities,
nextPageToken: cursor
});
});
});
books/curd.js
router.get('/', function list(req, res) {
model.list(10, req.query.pageToken,
function(err, entities, cursor) {
if (err) { return handleRpcError(err, res); }
res.render('books/list.jade', {
books: entities,
nextPageToken: cursor
});
}
);
});
these 2 codes are similar, but I don't know why these similar codes comes up. I think crud.js enough, but why api.js comes up. Could you explain how these 2 codes work?
In this sample application, there are two interface:
graphic user interface (GUI) - curd.js handles generating HTML that is rendered later in the browser (in our case jade tempting language is involved)
application programming interface (API) - api.js provides the way to interact with application programmatically, without browser (ex: create new record in database, or query some data by making specific call to particular route)
For deeper understanding I would suggest learning more about express.js, that will give better idea what those outputs are.
P.S. Welcome to MEAN world :)

How to parse page that uses HTML5 local storage?

In advance sorry for my English)
I have a task - write a parser for site, but all his pages save entered data in HTML5 local storage. Its really to emulate click on images on pages and retrieve all variables values that was saved to data storage after this click? For example, using NodeJS + parser like jsdom (https://github.com/tmpvar/jsdom)? Or i can use some alternatively technologies for this?
Thank you!
Sounds like you are trying to parse a website with lots of javascript. You can use phontom to simulate user behaviour. Consider you want to use node. Then you can use Node-Phontom to do that.
var phantom=require('node-phantom');
phantom.create(function(err,ph) {
return ph.createPage(function(err,page) {
return page.open("you/url/", function(err,status) {
console.log("opened site? ", status);
page.includeJs('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', function(err) {
//jQuery Loaded.
//Settimeout to wait for a bit for AJAX call.
setTimeout(function() {
return page.evaluate(function() {
//Get what you want from the page
//e.g. localStorage.getItem('xxx');
}, 5000);
});
});
});
});
Here is phontom.
Here is node-phontom.

Resources