2

I have NodeJs/Express app in which I would like to open new browser window and render local EJS view into it. I am trying to do it using Puppeteer.

const puppeteer = require('puppeteer');
router.post('/new_window', async (req, res) => {
  try {
     const browser = await puppeteer.launch({headless: false});
     const page = await browser.newPage();
     const pageContent = ejs.render('../views/mypage.ejs', {})  
     await page.setContent(pageContent)
     //await page.screenshot({path: 'example.png'});
     // await browser.close();
  } catch (err) {
     res.status(500)
     console.log(err)
     res.send(err.message)
  }
})

In the browser instead of page layout I get:

../views/mypage.ejs
ggorlen
  • 44,755
  • 7
  • 76
  • 106
goryef
  • 1,337
  • 3
  • 22
  • 37

2 Answers2

2

Instead of:

await page.goto(...); // This code is acting like your browser's address bar

Try

const pageContent = ejs.render('../views/mypage.ejs', {data to populate your .ejs page}) //This is sudo code. Check ejs docs on how to do this 
await page.setContent(pageContent)

The code above will let you create your page on your server. With page.setContent(..)you can load any string of HTML.

  • Thank You. I don't get any errors now. The browser opens, but instead of expected page layout, i get just a text: ../views/mypage.ejs. Any ideas what am I missing? – goryef Feb 26 '20 at 17:18
  • You need to turn your view's contents into a string. Puppeteer doesn't know how to handle a ejs file but it can handle HTML as a string. For example this should work: `page.setContent('

    Test

    ')`. You need to get the contents of a your mypage and pass it to `page.setContents()`
    –  Feb 26 '20 at 18:54
  • Saw you updated the code in the question. You need to find a `ejs.render` method that take a file path, like `ejs.renderFile` –  Feb 26 '20 at 18:57
  • Got it. I get the layout now, but it looks like none of the links for scripts and css are working. – goryef Feb 26 '20 at 19:08
  • 1
    yeah this is the beginning of "Server Side Rendering" a page. Its a lot of work. Another way of looking at this problem that is less work. Is make another Express app that holds your puppeteer logic. Then run your original website. Then in the puppeteer app you can use `page.goto('http://')` –  Feb 26 '20 at 19:23
  • 1
    "This is sudo code". You mean pseudo code :D – Design by Adrian Apr 05 '22 at 09:28
  • This is incorrect. `ejs.render('../views/mypage.ejs')` will still render the literal string `'../views/mypage.ejs'` as the template contents, same problem as OP. You probably want `renderFile`, which will treat the path as a file. But then you also need to `await` it. – ggorlen Nov 04 '22 at 21:38
-1

OP made an edit that correctly uses page.setContent rather than page.goto, however, there's still an issue. ejs.render() is used to run EJS on a template in string form, so it's treating the file path as the template itself. If you want to read the file into a string first (possibly when your app starts, if the template never changes), ejs.render() will work.

The other approach is to use the EJS method that accepts a path, ejs.renderFile(). Here's a minimal example showing the usage:

const ejs = require("ejs"); // 3.1.8
const express = require("express"); // ^4.18.1
const puppeteer = require("puppeteer"); // ^19.1.0

express()
.get("/greet", (req, res) => {
  let browser;
  (async () => {
    browser = await puppeteer.launch();
    const [page] = await browser.pages();
    const html = await ejs.renderFile("greet.ejs", {name: "world"});
    await page.setContent(html);
    const buf = await page.screenshot();
    res.contentType("image/png");
    res.send(buf);
  })()
    .catch(err => {
      console.error(err);
      res.sendStatus(500);
    }) 
    .finally(() => browser?.close());
})
.listen(3000);

Where greet.ejs contains:

<!DOCTYPE html>
<html>
<body>
<h1>Hello, <%- name %></h1>
</body>
</html>

To make a PDF with Express, EJS and Puppeteer, see Express/Puppeteer: generate PDF from EJS template and send as response.

To reuse the browser across routes, see this answer for a possible approach.

ggorlen
  • 44,755
  • 7
  • 76
  • 106